question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

How to extend an existing chart?

See original GitHub issue

Im looking to currently change the draw() function of the doughnut to add rounded ends. I have managed to kinda get it working with a registered plugin but not for all my datasets. Is there a way to create my own version of the doughnut?

I know in pure JS i could use something like

Chart.elements.Arc.prototype.draw = function() {
  var ctx = this._chart.ctx;
  var vm = this._view;
  var sA = vm.startAngle;
  var eA = vm.endAngle;

  ctx.beginPath();
  ctx.arc(vm.x, vm.y, vm.outerRadius, sA + window.arcSpacing, eA - window.arcSpacing);
  ctx.strokeStyle = vm.backgroundColor;
  ctx.lineWidth = vm.borderWidth;
  ctx.lineCap = 'round';
  ctx.setLineDash([15]);
  ctx.stroke();
  ctx.closePath();
};

But im not clear on how to actually extend and create my own chart to use that draw in react-chart-js.

Issue Analytics

  • State:closed
  • Created 5 years ago
  • Comments:8

github_iconTop GitHub Comments

6reactions
danielnmaicommented, Jan 29, 2019

I’m working on a bar chart with customized rounded corners, not doughnut but I was able to extend the draw function with react-chartjs-2. The below code may give you some ideas on how to get it done.

In order to extend the draw function, usually you need to use componentWillMount(), but it’s going to be deprecated in the future, so constructor() is one place to draw before render.

To have rounded bars in bar chart, you would need do something like this:

import React from 'react';
import { Bar, Chart } from 'react-chartjs-2';


class BarChart extends React.Component {
  constructor(props) {
    super(props);
  }

Chart.helpers.extend(Chart.elements.Rectangle.prototype, {
      draw() {
        const { ctx } = this._chart;
        const vm = this._view;
        let { borderWidth } = vm;

        let left; let right; let top; let bottom; let signX; let signY; let borderSkipped; let
          radius;

        // If radius is less than 0 or is large enough to cause drawing errors a max
        //      radius is imposed. If cornerRadius is not defined set it to 0.
        let { cornerRadius } = this._chart.config.options;
        if (cornerRadius < 0) { cornerRadius = 0; }

        if (typeof cornerRadius === 'undefined') { cornerRadius = 0; }

        if (!vm.horizontal) {
          // bar
          left = vm.x - vm.width / 2;
          right = vm.x + vm.width / 2;
          top = vm.y;
          bottom = vm.base;
          signX = 1;
          signY = bottom > top ? 1 : -1;
          borderSkipped = vm.borderSkipped || 'bottom';
        } else {
          // horizontal bar
          left = vm.base;
          right = vm.x;
          top = vm.y - vm.height / 2;
          bottom = vm.y + vm.height / 2;
          signX = right > left ? 1 : -1;
          signY = 1;
          borderSkipped = vm.borderSkipped || 'left';
        }

        // Canvas doesn't allow us to stroke inside the width so we can
        // adjust the sizes to fit if we're setting a stroke on the line
        if (borderWidth) {
          // borderWidth shold be less than bar width and bar height.
          const barSize = Math.min(Math.abs(left - right), Math.abs(top - bottom));
          borderWidth = borderWidth > barSize ? barSize : borderWidth;
          const halfStroke = borderWidth / 2;
          // Adjust borderWidth when bar top position is near vm.base(zero).
          const borderLeft = left + (borderSkipped !== 'left' ? halfStroke * signX : 0);
          const borderRight = right + (borderSkipped !== 'right' ? -halfStroke * signX : 0);
          const borderTop = top + (borderSkipped !== 'top' ? halfStroke * signY : 0);
          const borderBottom = bottom + (borderSkipped !== 'bottom' ? -halfStroke * signY : 0);
          // not become a vertical line?
          if (borderLeft !== borderRight) {
            top = borderTop;
            bottom = borderBottom;
          }
          // not become a horizontal line?
          if (borderTop !== borderBottom) {
            left = borderLeft;
            right = borderRight;
          }
        }

        ctx.beginPath();
        ctx.fillStyle = vm.backgroundColor;
        ctx.strokeStyle = vm.borderColor;
        ctx.lineWidth = borderWidth;

        // Corner points, from bottom-left to bottom-right clockwise
        // | 1 2 |
        // | 0 3 |
        const corners = [
          [left, bottom],
          [left, top],
          [right, top],
          [right, bottom],
        ];

        // Find first (starting) corner with fallback to 'bottom'
        const borders = ['bottom', 'left', 'top', 'right'];
        let startCorner = borders.indexOf(borderSkipped, 0);
        if (startCorner === -1) {
          startCorner = 0;
        }

        function cornerAt(index) {
          return corners[(startCorner + index) % 4];
        }

        // Draw rectangle from 'startCorner'
        let corner = cornerAt(0);
        ctx.moveTo(corner[0], corner[1]);

        for (let i = 1; i < 4; i += 1) {
          corner = cornerAt(i);
          let nextCornerId = i + 1;
          if (nextCornerId === 4) {
            nextCornerId = 0;
          }

          const width = corners[2][0] - corners[1][0];
          const height = corners[0][1] - corners[1][1];
          const x = corners[1][0];
          const y = corners[1][1];

          radius = cornerRadius;
          // Fix radius being too large
          if (radius > Math.abs(height) / 2) {
            radius = Math.floor(Math.abs(height) / 2);
          }
          if (radius > Math.abs(width) / 2) {
            radius = Math.floor(Math.abs(width) / 2);
          }

          if (height < 0) {
            // Negative values in a standard bar chart
            const xTl = x; const xTr = x + width;
            const yTl = y + height; const yTr = y + height;

            const xBl = x; const xBr = x + width;
            const yBl = y; const yBr = y;

            // Draw
            ctx.moveTo(xBl + radius, yBl);
            ctx.lineTo(xBr - radius, yBr);
            ctx.quadraticCurveTo(xBr, yBr, xBr, yBr - radius);
            ctx.lineTo(xTr, yTr + radius);
            ctx.quadraticCurveTo(xTr, yTr, xTr - radius, yTr);
            ctx.lineTo(xTl + radius, yTl);
            ctx.quadraticCurveTo(xTl, yTl, xTl, yTl + radius);
            ctx.lineTo(xBl, yBl - radius);
            ctx.quadraticCurveTo(xBl, yBl, xBl + radius, yBl);
          } else if (width < 0) {
            // Negative values in a horizontal bar chart
            const xTl = x + width; const xTr = x;
            const yTl = y; const yTr = y;

            const xBl = x + width; const xBr = x;
            const yBl = y + height; const yBr = y + height;

            // Draw
            ctx.moveTo(xBl + radius, yBl);
            ctx.lineTo(xBr - radius, yBr);
            ctx.quadraticCurveTo(xBr, yBr, xBr, yBr - radius);
            ctx.lineTo(xTr, yTr + radius);
            ctx.quadraticCurveTo(xTr, yTr, xTr - radius, yTr);
            ctx.lineTo(xTl + radius, yTl);
            ctx.quadraticCurveTo(xTl, yTl, xTl, yTl + radius);
            ctx.lineTo(xBl, yBl - radius);
            ctx.quadraticCurveTo(xBl, yBl, xBl + radius, yBl);
          } else {
            // Positive Value
            ctx.moveTo(x + radius, y);
            ctx.lineTo(x + width - radius, y);
            ctx.quadraticCurveTo(x + width, y, x + width, y + radius);
            ctx.lineTo(x + width, y + height - radius);
            ctx.quadraticCurveTo(x + width, y + height, x + width - radius, y + height);
            ctx.lineTo(x + radius, y + height);
            ctx.quadraticCurveTo(x, y + height, x, y + height - radius);
            ctx.lineTo(x, y + radius);
            ctx.quadraticCurveTo(x, y, x + radius, y);
          }
        }

        ctx.fill();
        if (borderWidth) {
          ctx.stroke();
        }
      },
    });
}

And then add a cornerRadius value to the options object, like this:

render() {
    return (
          <Bar
            data={data}
            options={{ cornerRadius: 10 }}
          />
    );
}
3reactions
aemccommented, Jul 17, 2019

How do you replace var ctx = this._chart.ctx; var vm = this._view; when using React hooks?

Read more comments on GitHub >

github_iconTop Results From Across the Web

Update the data in an existing chart - Microsoft Support
Changes you make will instantly show up in the chart. Right-click the item you want to change and input the data--or type a...
Read more >
Create Helm chart extending an existing chart - Stack Overflow
I want to extend the Gitlab helm chart, to do so I am adding it as a requirement of my own chart. The...
Read more >
Modify Excel Chart Data Range - CustomGuide
1. Select the chart 2. Click the Design tab. 3. Click the Select Data button.
Read more >
How to Extend a Trendline on Excel
Because there are a variety of charts available, such as line and bar, determine the best chart style to suit the type of...
Read more >
How to extend Chart capabilities using AdvancedFormat?
OutSystems Charts are based on the Highcharts API and, have an option to extend the capabilities of the existing chart component by adding...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found