3👍
The rounded ends of the arcs in a doughnut or pie chart are covered by the borderRadius option.
If we choose the radius of that rounded end to be 15px we can write that as
datasets: [{
//..... data and other dataset options
borderRadius: [
{outerStart: 0, outerEnd: 15, innerStart: 0, innerEnd: 15},
0
]
}]
Obviously, to round the other end, one should set outerStart
and innerStart
values.
The second value of the borderRadius
array (zero) corresponds to second value of the dataset, which stands as the background of the stripe.
Here’s a snippet with your data
const data = {
datasets: [{
label: 'Dataset 1',
data: [50, 50],
backgroundColor: [
"#ff3333",
"#660000",
],
borderRadius: [
{outerStart: 0, outerEnd: 15, innerStart: 0, innerEnd: 15},
0
],
borderWidth: 0
},
{
label: 'Dataset 2',
data: [70, 30],
backgroundColor: [
"#00ff00",
"#003300",
],
borderRadius: [
{outerStart: 0, outerEnd: 15, innerStart: 0, innerEnd: 15},
0
],
borderWidth: 0
},
{
label: 'Dataset 3',
data: [40, 60],
backgroundColor: [
"#1991EB",
"#001a4d",
],
borderRadius: [
{outerStart: 0, outerEnd: 15, innerStart: 0, innerEnd: 15},
0
],
borderWidth: 0,
}]
};
const chart = new Chart(document.querySelector("#myChart"), {
type: 'doughnut',
data,
options: {
cutout: 60,
responsive: true,
radius: "80%"
}
});
<div style="max-height:350px">
<canvas id="myChart" style="background-color: #000"></canvas>
</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/4.3.0/chart.umd.js"
integrity="sha512-CMF3tQtjOoOJoOKlsS7/2loJlkyctwzSoDK/S40iAB+MqWSaf50uObGQSk5Ny/gfRhRCjNLvoxuCvdnERU4WGg=="
crossorigin="anonymous" referrerpolicy="no-referrer"></script>
There are a few problems with this:
- the background is not perfect, under the rounded ends there’s the canvas background, not the second color that’s supposed to e the stripe background
- the second item, that should act as a background is also producing a tooltip on hover, that needs to be disabled
- the radius of the rounded end has to be preset, and a resize of the canvas may get to inadequate values (although an update can be implemented).
To cover these cases, I implemented a custom DoughnutController
, see the docs, called DoughnutWithRoundEnds
, with id doughnut_round_ends
, that extends the standard doughnut
with three dataset options:
roundStart
androundEnd
, (default valuetrue
for each), a boolean for each value (no need to set the border radius, it is computed automatically taking into account the current size of the arc when drawn)overallBackgroundColor
– the inactive background color of the stripe – which will allow to use just one value in combination with setting acircumference
const _drawArcElement = Chart.ArcElement.prototype.draw;
class DoughnutWithRoundEnds extends Chart.DoughnutController{
static id = "doughnut_round_ends";
updateElements(arcs, start, count, mode) {
super.updateElements(arcs, start, count, mode);
const dataset = this.getDataset();
const overallBackgroundColor = dataset.overallBackgroundColor ??
this.options.overallBackgroundColor;
let roundEnd = this.getDataset().roundEnd ?? this.options.roundEnd ?? true;
if(!Array.isArray(roundEnd)){
roundEnd = Array(arcs.length).fill(roundEnd);
}
let roundStart = dataset.roundStart ?? this.options.roundStart ?? true;
if(!Array.isArray(roundStart)){
roundStart = Array(arcs.length).fill(roundStart);
}
if(overallBackgroundColor && roundEnd.length > 0){
arcs.forEach((arc, i) => {
let borderRadius = 0;
if(roundEnd[i] || roundStart[i]){
const r = Math.ceil((arc.outerRadius - arc.innerRadius)/2),
rStart = roundStart[i] ? r : 0,
rEnd = roundEnd[i] ? r : 0;
borderRadius = {
outerStart: rStart,
outerEnd: rEnd,
innerStart: rStart,
innerEnd: rEnd
};
}
if(i === 0 && overallBackgroundColor){
arc.draw = function(ctx){
const backgroundColor = this.options.backgroundColor,
startAngle = this.startAngle,
endAngle = this.endAngle;
this.options.backgroundColor = overallBackgroundColor;
this.options.borderRadius = 0;
this.startAngle = 0;
this.endAngle = 2*Math.PI;
_drawArcElement.call(this, ctx);
this.options.backgroundColor = backgroundColor;
this.startAngle = startAngle;
this.endAngle = endAngle;
this.options.borderRadius = borderRadius;
_drawArcElement.call(this, ctx);
}
}
else{
this.options.borderRadius = borderRadius;
}
});
}
}
}
Chart.register(DoughnutWithRoundEnds);
const data = {
datasets: [{
label: 'Dataset 1',
data: [50],
circumference: [50/100*360],
backgroundColor: [
"#ff3333"
],
//roundStart: [true], //default
// roundEnd: [true], // default
overallBackgroundColor: "#660000",
borderWidth: 0
},
{
label: 'Dataset 2',
data: [70],
circumference: [70/100*360],
backgroundColor: [
"#00ff00"
],
overallBackgroundColor: "#003300",
borderWidth: 0
},
{
label: 'Dataset 3',
data: [40],
circumference: [40/100*360],
backgroundColor: [
"#1991EB",
],
overallBackgroundColor: "#001a4d",
borderWidth: 0
}]
};
const chart = new Chart(document.querySelector("#myChart"), {
type: 'doughnut_round_ends',
data,
options: {
cutout: 60,
responsive: true,
radius: "80%",
//roundStart: false // for all datasets
}
});
<div style="max-height:350px">
<canvas id="myChart" style="background-color: #000"></canvas>
</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/Chart.js/4.3.0/chart.umd.js"
integrity="sha512-CMF3tQtjOoOJoOKlsS7/2loJlkyctwzSoDK/S40iAB+MqWSaf50uObGQSk5Ny/gfRhRCjNLvoxuCvdnERU4WGg=="
crossorigin="anonymous" referrerpolicy="no-referrer"></script>
This version also works with two (or more) values, as in the first case, if the second one is relevant and isn’t just for the background: jsFiddle.