skip to Main Content

I have a regular Chartjs doughnut chart with multiple datasets, using this code for the dataset:

datasets: 
    [
        {
            label: 'Bugs',
            data: [ 60 , 6.6666666666667 , 33.333333333333 ],
            backgroundColor: ['#25CFE4', '#92E7F1', '#eeeeee'],
        }, {
            label: 'Fixes',
            data: [ 60 , 0.44444444444444 , 39.555555555556 ],
            backgroundColor: ['#514463', '#8C75AB', '#eeeeee'],
        }, {
            label: 'Redesigns',
            data: [
            33.333333333333 , 10.37037037037 , 56.296296296296 ],
            backgroundColor: ['#1B745F', '#40C1A0', '#eeeeee'],
        }
    ]
};

I am trying to implement rounded edges on the scales, I manage to make the first one round, but no luck with the others.

Basically, this is what I have now

enter image description here

And this is what I am trying to achieve (sorry for the poor photoshop)

enter image description here

I don’t mind if the start of the scale is also round or the grey area (which I painted grey just to give the impression of something not yet filled) gas round edges too.

Thanks

3

Answers


  1. This is not the exact answer that you might be looking for but this was my requirement to get rounded edges for multiple datasets. This one rounds off one color in each doughnut dataset.

    I used the answer at Chart.js Doughnut with rounded edges and text centered and made some changes. Here is the code:

            // round corners
        Chart.pluginService.register({
            afterUpdate: function (chart) {
                if (chart.config.options.elements.arc.roundedCornersFor !== undefined) {
                    var a=chart.config.data.datasets.length -1;
                    for (let i in chart.config.data.datasets) {
                        var arc = chart.getDatasetMeta(i).data[chart.config.options.elements.arc.roundedCornersFor];
    
                        arc.round = {
                            x: (chart.chartArea.left + chart.chartArea.right) / 2,
                            y: (chart.chartArea.top + chart.chartArea.bottom) / 2,
                            radius: chart.innerRadius + chart.radiusLength / 2 + (a * chart.radiusLength),
                            thickness: chart.radiusLength / 2 - 1,
                            backgroundColor: arc._model.backgroundColor
                        }
                        a--;
                    }
                }
            },
    
            afterDraw: function (chart) {
                if (chart.config.options.elements.arc.roundedCornersFor !== undefined) {
                    var ctx = chart.chart.ctx;
                    for (let i in chart.config.data.datasets) {
                        var arc = chart.getDatasetMeta(i).data[chart.config.options.elements.arc.roundedCornersFor];
                        var startAngle = Math.PI / 2 - arc._view.startAngle;
                        var endAngle = Math.PI / 2 - arc._view.endAngle;
    
                        ctx.save();
                        ctx.translate(arc.round.x, arc.round.y);
                        console.log(arc.round.startAngle)
                        ctx.fillStyle = arc.round.backgroundColor;
                        ctx.beginPath();
                        ctx.arc(arc.round.radius * Math.sin(startAngle), arc.round.radius * Math.cos(startAngle), arc.round.thickness, 0, 2 * Math.PI);
                        ctx.arc(arc.round.radius * Math.sin(endAngle), arc.round.radius * Math.cos(endAngle), arc.round.thickness, 0, 2 * Math.PI);
                        ctx.closePath();
                        ctx.fill();
                        ctx.restore();
                    }
                }
            },
        });
    

    Fiddle : http://jsfiddle.net/n6vLv1zv/

    Hope it brings you one step closer.

    Login or Signup to reply.
  2. Made a few more adjustments and finally got it.This does exactly what you want:

        Chart.pluginService.register({
            afterUpdate: function (chart) {
                    var a=chart.config.data.datasets.length -1;
                    for (let i in chart.config.data.datasets) {
                        for(var j = chart.config.data.datasets[i].data.length - 1; j>= 0;--j) { 
                            if (Number(j) == (chart.config.data.datasets[i].data.length - 1))
                                continue;
                            var arc = chart.getDatasetMeta(i).data[j];
                            arc.round = {
                                x: (chart.chartArea.left + chart.chartArea.right) / 2,
                                y: (chart.chartArea.top + chart.chartArea.bottom) / 2,
                                radius: chart.innerRadius + chart.radiusLength / 2 + (a * chart.radiusLength),
                                thickness: chart.radiusLength / 2 - 1,
                                backgroundColor: arc._model.backgroundColor
                            }
                        }
                        a--;
                    }
            },
    
            afterDraw: function (chart) {
                    var ctx = chart.chart.ctx;
                    for (let i in chart.config.data.datasets) {
                        for(var j = chart.config.data.datasets[i].data.length - 1; j>= 0;--j) { 
                            if (Number(j) == (chart.config.data.datasets[i].data.length - 1))
                                continue;
                            var arc = chart.getDatasetMeta(i).data[j];
                            var startAngle = Math.PI / 2 - arc._view.startAngle;
                            var endAngle = Math.PI / 2 - arc._view.endAngle;
    
                            ctx.save();
                            ctx.translate(arc.round.x, arc.round.y);
                            console.log(arc.round.startAngle)
                            ctx.fillStyle = arc.round.backgroundColor;
                            ctx.beginPath();
                            //ctx.arc(arc.round.radius * Math.sin(startAngle), arc.round.radius * Math.cos(startAngle), arc.round.thickness, 0, 2 * Math.PI);
                            ctx.arc(arc.round.radius * Math.sin(endAngle), arc.round.radius * Math.cos(endAngle), arc.round.thickness, 0, 2 * Math.PI);
                            ctx.closePath();
                            ctx.fill();
                            ctx.restore();
                        }
                    }
            },
        });
    

    Fiddle – http://jsfiddle.net/tgyxmkLj/1/

    Login or Signup to reply.
  3. I understand this is an older thread, but I have a similar design problem using "chart.js": "^3.9.1" and "react-chartjs-2": "^4.3.1". My solution was to add 2 "caps" to each arc: one serves as the rounded-spacing between each arcs and one is the rounded cap. Here’s my full code with typings:

    import {
      ArcElement as ChartJSArcElement,
      ArcOptions as ChartJSArcOptions,
      ArcProps as ChartJSArcProps,
      Color as ChartJSColor,
      Chart as ChartJS,
      ChartData as ChartJSData,
      DoughnutController as ChartJSDoughnutController,
      Element as ChartJSElement,
      Plugin as ChartJSPlugin,
    } from "chart.js";
    import { AnyObject as ChartJSAnyObject } from "chart.js/types/basic";
    import { Doughnut } from "react-chartjs-2";
    
    ChartJS.register(ChartJSArcElement);
    
    const colors: ChartJSColor[] = ["blue", "red", "green", "yellow"];
    const data: ChartJSData<"doughnut"> = {
      labels: colors,
      datasets: [
        {
          data: [500, 100, 300, 250],
          backgroundColor: colors,
          borderWidth: 0,
        },
      ],
    };
    
    interface Round {
      x: number;
      y: number;
      radius: number;
      arcColor: ChartJSColor;
    }
    
    type CustomElement =
      | (ChartJSElement<
          ChartJSAnyObject | ChartJSArcProps,
          ChartJSAnyObject | ChartJSArcOptions
        > & {
          round?: Round;
        })
      | (ChartJSArcElement<ChartJSArcProps, ChartJSArcOptions> & {
          round?: Round;
        });
    
    const plugins: ChartJSPlugin<"doughnut">[] = [
      {
        id: "arcCaps",
        afterUpdate: function (chart) {
          // we only expect 1 dataset
          const { data, controller } = chart.getDatasetMeta(0);
          const { outerRadius, innerRadius } =
            controller as ChartJSDoughnutController;
    
          for (let i = data.length - 1; i >= 0; --i) {
            const arc: CustomElement = data[i];
    
            // determine total radius by diffing outer values
            const radiusLength = outerRadius - innerRadius;
    
            arc.round = {
              // chart's x/y lengths
              x: (chart.chartArea.left + chart.chartArea.right) / 2,
              y: (chart.chartArea.top + chart.chartArea.bottom) / 2,
              // radius of a single arc
              radius: innerRadius + radiusLength / 2,
              arcColor: arc.options.backgroundColor as ChartJSColor,
            };
          }
        },
    
        afterDraw: function (chart) {
          const { ctx } = chart;
    
          // we only expect 1 dataset
          const { data } = chart.getDatasetMeta(0);
    
          // iterate through each arc's data point
          for (let i = data.length - 1; i >= 0; --i) {
            // extract data
            const arc: CustomElement = data[i];
            const round = arc.round as Round;
            const props = (
              arc as ChartJSArcElement<ChartJSArcProps, ChartJSArcOptions>
            ).getProps([
              "startAngle",
              "endAngle",
              "innerRadius",
              "outerRadius",
              "circumference",
            ]);
    
            // determine end angle of arc within the donut shape
            const endAngle = Math.PI / 2 - props.endAngle;
    
            ctx.save();
            ctx.translate(round.x, round.y);
    
            // generate white arc that serves as padding (assumes background is white)
            ctx.fillStyle = "white";
            ctx.beginPath();
            ctx.arc(
              round.radius * Math.sin(endAngle),
              round.radius * Math.cos(endAngle),
              // twice as wide to smooth out the padding's angles
              props.outerRadius - props.innerRadius,
              // cap should "face" outward from arc's outer boundary
              0 + props.endAngle,
              Math.PI + props.endAngle
            );
            ctx.closePath();
            ctx.fill();
    
            // generate cap
            ctx.fillStyle = round.arcColor;
            ctx.beginPath();
            ctx.arc(
              round.radius * Math.sin(endAngle),
              round.radius * Math.cos(endAngle),
              (props.outerRadius - props.innerRadius) / 2,
              // draw a full circle for the cap to prevent any spacing issue
              0,
              Math.PI * 2
            );
            ctx.closePath();
            ctx.fill();
    
            ctx.restore();
          }
        },
      },
    ];
    
    export default function App() {
      return (
        <div>
          <Doughnut
            width={300}
            height={300}
            data={data}
            plugins={plugins}
            options={{
              maintainAspectRatio: false,
              responsive: true,
              cutout: "90%",
              plugins: {
                tooltip: { enabled: false },
                legend: { display: false },
              },
              elements: {
                arc: { borderWidth: 0 },
              },
            }}
          />
        </div>
      );
    }
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search