Chart.js (v3) Doughnut with rounded edges, but not everywhere
Asked Answered
R

1

0

I know there are a few answer for this, but it seems this one is a bit different. I need to change doughnut chart, rounded the first one and the last but one too. So in my example the black (first dataset) only would be rounded on the beginning (one side) and the gray (last but one) would be rounded at the end, like on the picture. enter image description here

Of course, this is the latest version (v3) of Chart.js.

I used some code from here: Chart.js Doughnut with rounded edges and text centered

Maybe it's better with a custom chart, but I couldn't even get this far with that.

This is my code so far. Only makes rounded the first dataset and unfortunately both sided of it.

Chart.defaults.elements.arc.borderWidth = 0;
Chart.defaults.elements.arc.roundedCornersFor = 0;
Chart.defaults.elements.arc.hoverBorderColor = 'white';

Chart.defaults.datasets.doughnut.cutout = '85%';

var doughnutChart = document.getElementById('doughnutChart');

if(doughnutChart) {
    new Chart(doughnutChart, {
        type: 'doughnut',
    
        // The data for our dataset
        data: {
            labels: [
                'Label 1',
                'Label 2',
                'Label 3',
                'Label 4'
              ],
              datasets: [{
                label: 'My First Dataset',
                data: [22, 31, 26, 19],
                backgroundColor: [
                  '#000000',
                  '#ffff00',
                  '#aaaaaa',
                  '#ff0000'
                ]
            }]
        },
        plugins: [
            {
                afterUpdate: function(chart) {
                    if (chart.options.elements.arc.roundedCornersFor !== undefined) {
                        var arc = chart.getDatasetMeta(0).data[chart.options.elements.arc.roundedCornersFor];
                        
                        arc.round = {
                            x: (chart.chartArea.left + chart.chartArea.right) / 2,
                            y: (chart.chartArea.top + chart.chartArea.bottom) / 2,
                            radius: (arc.outerRadius + arc.innerRadius) / 2,
                            thickness: (arc.outerRadius - arc.innerRadius) / 2,
                            backgroundColor: arc.options.backgroundColor
                        }
                    }
                },
                afterDraw: (chart) => {
                    if (chart.options.elements.arc.roundedCornersFor !== undefined) {
                        var {ctx, canvas} = chart;
                        var arc = chart.getDatasetMeta(0).data[chart.options.elements.arc.roundedCornersFor];

                        var startAngle = Math.PI / 2 - arc.startAngle;
                        var endAngle = Math.PI / 2 - arc.endAngle;
        
                        ctx.save();
                        ctx.translate(arc.round.x, arc.round.y);
                        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();
                    }
                }
            }
        ]
    });
}
.container {
  width: 400px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/3.3.2/chart.min.js"></script>
<div class="container">
  <canvas id="doughnutChart"></canvas>
</div>
Rasorial answered 14/6, 2021 at 10:7 Comment(1)
Does this answer your question? Chart.js Doughnut with rounded edgesJacobs
C
2

I have modified your code and made changes for roundedCornersFor. It will now take an object structure which will define take start and end as keys and the values will be the arc positions which are according to labels.

Chart.defaults.elements.arc.roundedCornersFor = {
  "start": 0, //0th position of Label 1
  "end": 2 //2nd position of Label 2
};

Below is the entire code snippet:

Chart.defaults.elements.arc.borderWidth = 0;
Chart.defaults.elements.arc.roundedCornersFor = {
  "start": 0, //0th position of Label 1
  "end": 2 //2nd position of Label 2
};

//Can be provided as array:
//Chart.defaults.elements.arc.roundedCornersFor = {
//  "start": [0,1,2],
//  "end": 3
//};

Chart.defaults.elements.arc.hoverBorderColor = 'white';

Chart.defaults.datasets.doughnut.cutout = '85%';

var chartInstance = new Chart(document.getElementById("chartJSContainer"), {
  type: 'doughnut',

  // The data for our dataset
  data: {
    labels: [
      'Label 1',
      'Label 2',
      'Label 3',
      'Label 4'
    ],
    datasets: [{
      label: 'My First Dataset',
      data: [22, 31, 26, 19],
      backgroundColor: [
        '#000000',
        '#ffff00',
        '#aaaaaa',
        '#ff0000'
      ]
    }]
  },
  //Inline Options...
  /*  options: {
    elements: {
      arc: {
        roundedCornersFor: {
          "start": 1, //0th position of Label 1
          "end": 1 //2nd position of Label 2
        }
      }
    }
  }, */
  plugins: [{
    afterUpdate: function(chart) {
      if (chart.options.elements.arc.roundedCornersFor !== undefined) {
        var arcValues = Object.values(chart.options.elements.arc.roundedCornersFor);

        arcValues.forEach(function(arcs) {
          arcs = Array.isArray(arcs) ? arcs : [arcs];
          arcs.forEach(function(i) {
            var arc = chart.getDatasetMeta(0).data[i];
            arc.round = {
              x: (chart.chartArea.left + chart.chartArea.right) / 2,
              y: (chart.chartArea.top + chart.chartArea.bottom) / 2,
              radius: (arc.outerRadius + arc.innerRadius) / 2,
              thickness: (arc.outerRadius - arc.innerRadius) / 2,
              backgroundColor: arc.options.backgroundColor
            }
          });
        });
      }
    },
    afterDraw: (chart) => {

      if (chart.options.elements.arc.roundedCornersFor !== undefined) {
        var {
          ctx,
          canvas
        } = chart;
        var arc,
          roundedCornersFor = chart.options.elements.arc.roundedCornersFor;
        for (var position in roundedCornersFor) {
          var values = Array.isArray(roundedCornersFor[position]) ? roundedCornersFor[position] : [roundedCornersFor[position]];
          values.forEach(p => {
            arc = chart.getDatasetMeta(0).data[p];
            var startAngle = Math.PI / 2 - arc.startAngle;
            var endAngle = Math.PI / 2 - arc.endAngle;
            ctx.save();
            ctx.translate(arc.round.x, arc.round.y);
            ctx.fillStyle = arc.options.backgroundColor;
            ctx.beginPath();
            if (position == "start") {
              ctx.arc(arc.round.radius * Math.sin(startAngle), arc.round.radius * Math.cos(startAngle), arc.round.thickness, 0, 2 * Math.PI);
            } else {
              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();
          });

        };
      }
    }
  }]
});
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/chart.js"></script>

<body>
  <canvas id="chartJSContainer" width="200" height="200"></canvas>
</body>
Commissure answered 14/6, 2021 at 15:8 Comment(5)
Just one remaining question. What if I want to define the rounded corner values within config? Like elements: {arc: {roundedCornersFor: {'start': 0,'end': 1}}}, It doesn't override.Rasorial
it doesn't for me unfortunately, but I keep trying. And it's also weird, that on hover (e.g. gray section), the background color doesn't change along with the other part.Rasorial
I have updated my answer and added inline options so in config the roundedCorners part will work and didn't understood your point about 'background doesn't change'.Commissure
Thanks again! I meant that when you roll over the gray area, it will become darker expect a circle at the end where it connects to the red area.Rasorial
I have updated that part in my code, so if you run the snippet the round and hover arc color will match. You will need to understand the logic and make changes accordingly from here on.Commissure

© 2022 - 2024 — McMap. All rights reserved.