function roundNumbersToSomethingReasonable(strings) {
return strings.reduce((acc, str, i) => acc + Math.round(arguments[i] * 1000) / 1000 + str.replace(/\s+/g, " "));
}
function circleSegment(x, y, radius, inner = 0, startAngle = 0, endAngle = 360) {
startAngle *= Math.PI / 180;
endAngle *= Math.PI / 180;
inner = Math.max(inner, 0);
const s0 = Math.sin(startAngle),
c0 = Math.cos(startAngle),
s1 = Math.sin(endAngle),
c1 = Math.cos(endAngle),
largeArc = +(Math.abs(endAngle - startAngle) > Math.PI),
sweep = +(endAngle > startAngle);
return inner ?
roundNumbersToSomethingReasonable`M${x + c0 * radius} ${y + s0 * radius}
A${radius} ${radius} 0 ${largeArc} ${sweep} ${x + c1 * radius} ${y + s1 * radius}
L${x + c1 * inner} ${y + s1 * inner}
A${inner} ${inner} 0 ${largeArc} ${1^sweep} ${x + c0 * inner} ${y + s0 * inner}
z` :
roundNumbersToSomethingReasonable`M${x + c0 * radius} ${y + s0 * radius}
A${radius} ${radius} 0 ${largeArc} ${sweep} ${x + c1 * radius} ${y + s1 * radius}
L${x} ${y}
z`;
}
const data = [{
value: 30,
color: "red"
},
{
value: 10,
color: "green"
},
{
value: 17,
color: "blue"
}
];
const total = data.reduce((total, item) => total + item.value, 0);
let angle = -90;
this.foo.innerHTML = data.map(item => `<path fill="${item.color}" d="${circleSegment(0, 0, 50, 30, angle, angle += item.value / total * 360)}" />`)
.join("\n");
<svg id="foo" viewBox="-50 -50 100 100"></svg>