Chart js Donut segment is not displayed in one direction. First and last segments are not working as expected
Expectation : Need chart js donut rounded segments displayed in one direction with white border between the segments
Code
// Create a custom Doughnut type with rounded segments
Chart.defaults.RoundedDoughnut = Chart.helpers.clone(Chart.defaults.doughnut);
Chart.controllers.RoundedDoughnut = Chart.controllers.doughnut.extend({
draw: function(ease) {
var ctx = this.chart.ctx;
var easingDecimal = ease || 1;
var arcs = this.getMeta().data;
var borderWidth = 20; // Width of the white border for space between segments
Chart.helpers.each(arcs, function(arc, i) {
var vm = arc._view;
var startAngle = vm.startAngle;
var endAngle = vm.endAngle;
var radius = (vm.outerRadius + vm.innerRadius) / 2;
var thickness = (vm.outerRadius - vm.innerRadius) / 2;
ctx.save();
ctx.translate(vm.x, vm.y);
// Draw each arc segment with a white border to create spacing
ctx.beginPath();
ctx.arc(0, 0, radius, startAngle, endAngle);
ctx.lineWidth = thickness * 2 + borderWidth; // Increase width to add border
ctx.strokeStyle = '#FFFFFF'; // Set border color to white
ctx.lineCap = 'round'; // Ensure all segments are rounded on both ends
ctx.stroke();
// Draw inner colored arc over the white border to make it look like a gap
ctx.beginPath();
ctx.arc(0, 0, radius, startAngle, endAngle);
ctx.lineWidth = thickness * 2;
ctx.strokeStyle = vm.backgroundColor; // Set segment color
ctx.stroke();
ctx.restore();
});
}
});
// Initialize the chart
window.onload = function() {
new Chart(document.getElementById('usersChart'), {
type: 'RoundedDoughnut',
data: {
datasets: [{
data: [10, 10, 10, 10, 10, 10, 10, 10], // Adjust data values for even segments
backgroundColor: [
'#5da4e7', '#8fbbe7', '#addbf0', '#4b8de7',
'#4da466', '#8ec486', '#b3dba8', '#63b571'
],
borderWidth: 0
}]
},
options: {
cutoutPercentage: 70,
tooltips: {
enabled: false
} // Optional: Disable tooltips to prevent hover issues
}
});
};
<canvas id="usersChart" width="400" height="400"></canvas>
You should specify the chart.js version you are using, in case it is not the latest, and it doesn't appear to be, since the code of your custom controller is not compatible with the latest v4 version of chart.js.
In any case, the problem is conceptual, the idea of circular "deck of cards" with each card partially covered by the next, can't be implemented by drawing full cards, since this approach is bound to have one card fully visible and one card covered at both ends.
An alternative would be to draw half-cards, and this would work, except in the case when the sizes of the sectors are very small - then the visible half-sector is too much and sectors will overlap incorrectly.
The best solution seems to actually draw the overlapping part, using
context.fill
function, filling half circles, rather than
context.stroke
of very thick arcs with rounded ends.
Here's the implementation of that idea, using latest chart.js v4 compatible controller; it should be easy to adapt it to older versions:
class RoundedDoughnut extends Chart.controllers.doughnut {
static id = "roundedDoughnut";
draw(...args) {
super.draw(...args);
const ctx = this.chart.ctx;
const arcs = this.getMeta().data;
const borderWidth = 10;
const borderColor = 'white';
if(arcs.length === 0){
return;
}
ctx.save();
const arc = arcs[0];
ctx.strokeStyle = borderColor;
ctx.lineWidth = borderWidth;
ctx.beginPath();
ctx.arc(arc.x, arc.y, arc.innerRadius, 0, 2*Math.PI);
ctx.stroke();
ctx.beginPath();
ctx.arc(arc.x, arc.y, arc.outerRadius, 0, 2*Math.PI);
ctx.stroke();
for(const arc of arcs){
const startAngle = arc.startAngle,
radius = (arc.outerRadius + arc.innerRadius) / 2,
thickness = (arc.outerRadius - arc.innerRadius) / 2 - borderWidth;
const x0 = arc.x + radius * Math.cos(startAngle),
y0 = arc.y + radius * Math.sin(startAngle);
ctx.beginPath();
ctx.arc(x0, y0, thickness, 0, 2*Math.PI);
ctx.fillStyle = arc.options.backgroundColor;
ctx.strokeStyle = arc.options.backgroundColor;
ctx.lineWidth = borderWidth;
ctx.fill();
ctx.stroke();
ctx.beginPath();
ctx.arc(x0, y0, thickness + borderWidth, Math.PI + startAngle, 2*Math.PI + startAngle);
ctx.strokeStyle = borderColor;
ctx.stroke();
}
ctx.restore();
}
}
Chart.registry.controllers.register(RoundedDoughnut)
new Chart(document.getElementById('usersChart'), {
type: 'roundedDoughnut',
data: {
datasets: [{
data: [10, 10, 10, 10, 10, 10, 10, 10], // Adjust data values for even segments
backgroundColor: [
'#5da4e7', '#8fbbe7', '#addbf0', '#4b8de7',
'#4da466', '#8ec486', '#b3dba8', '#63b571'
],
borderWidth: 0
}]
},
options: {
cutout: "70%",
layout:{
padding: 10
},
plugins: {
tooltip: {
enabled: false
} // Optional: Disable tooltips to prevent hover issues
}
}
});
<div style="width:400px; height: 400px">
<canvas id="usersChart"></canvas>
</div>
<script src="https://cdn.jsdelivr.net/npm/chart.js"></script>
Also in a jsFiddle, with the features positioned at the other ends of the segments ("clockwise"), as requested.