I am trying to build a donut chart
I want a design similar the following image where the values are shown in between the coloured pie
JavaScript
x
24
24
1
import DonutChart from 'react-d3-donut';
2
3
let data = [{
4
count: 20 ,
5
color: "red" ,
6
name: 'My name'
7
},{
8
count:30,
9
color:"green",
10
name:"yeys"
11
},{
12
count:20,
13
color:"orange",
14
name:"yeys"
15
}]
16
<DonutChart
17
innerRadius={90}
18
outerRadius={100}
19
transition={true}
20
svgClass="example1"
21
pieClass="pie1"
22
displayTooltip={true}
23
strokeWidth={3}
24
data={data} />
JavaScript
1
2
1
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
2
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
Advertisement
Answer
Here is a simple donut chart with D3:
JavaScript
1
47
47
1
const data = [
2
{value: 20, text: 'First', color: 'red'},
3
{value: 30, text: 'Second', color: 'green'},
4
{value: 60, text: 'Third', color: 'blue'},
5
];
6
7
const svg = d3.select('svg');
8
const width = parseInt(svg.attr('width'));
9
const height = parseInt(svg.attr('height'));
10
11
const margin = 10;
12
const arcWidth = 30;
13
const radius = Math.min(width/2 - margin, height/2 - margin) - arcWidth / 2;
14
const center = {x: width / 2, y: height / 2};
15
16
let anglePos = 0;
17
const angleOffset = 0.025;
18
19
const sum = data.reduce((s, {value}) => s + value, 0);
20
data.forEach(({value, text, color}, index) => {
21
const angle = Math.PI * 2 * value / sum;
22
const start = {
23
x: center.x + radius * Math.sin(anglePos + angleOffset),
24
y: center.y + radius * -Math.cos(anglePos + angleOffset),
25
};
26
anglePos += angle;
27
const end = {
28
x: center.x + radius * Math.sin(anglePos - angleOffset),
29
y: center.y + radius * -Math.cos(anglePos -angleOffset),
30
};
31
const flags = value / sum >= 0.5 ? '1 1 1' : '0 0 1';
32
const pathId = `my-pie-chart-path-${index}`;
33
svg.append('path')
34
.attr('id', pathId)
35
.attr('d', `M ${start.x},${start.y} A ${radius},${radius} ${flags} ${end.x},${end.y}`)
36
.style('stroke', color)
37
.style('fill', 'none')
38
.style('stroke-width', arcWidth);
39
40
svg.append('text')
41
.attr('dy', 6)
42
.attr('text-anchor', 'middle')
43
.append('textPath')
44
.attr('startOffset', angle * radius / 2)
45
.attr('href', `#${pathId}`)
46
.text(text)
47
});
JavaScript
1
6
1
text {
2
font-family: Calibri;
3
font-size: 20px;
4
font-weight: bold;
5
fill: white;
6
}
JavaScript
1
3
1
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/6.7.0/d3.min.js"></script>
2
3
<svg width="200" height="200"></svg>
An example with text backgrounds:
JavaScript
1
71
71
1
const data = [
2
{value: 20, text: 'First', color: 'red'},
3
{value: 30, text: 'Second', color: 'green'},
4
{value: 60, text: 'Third', color: 'blue'},
5
];
6
7
const svg = d3.select('svg');
8
const width = parseInt(svg.attr('width'));
9
const height = parseInt(svg.attr('height'));
10
11
const margin = 10;
12
const arcWidth = 30;
13
const radius = Math.min(width/2 - margin, height/2 - margin) - arcWidth / 2;
14
const center = {x: width / 2, y: height / 2};
15
16
let anglePos = 0;
17
const angleOffset = 0.025;
18
19
const sum = data.reduce((s, {value}) => s + value, 0);
20
data.forEach(({value, text, color}, index) => {
21
const angle = Math.PI * 2 * value / sum;
22
const start = {
23
x: center.x + radius * Math.sin(anglePos + angleOffset),
24
y: center.y + radius * -Math.cos(anglePos + angleOffset),
25
};
26
anglePos += angle;
27
const end = {
28
x: center.x + radius * Math.sin(anglePos - angleOffset),
29
y: center.y + radius * -Math.cos(anglePos -angleOffset),
30
};
31
const flags = value / sum >= 0.5 ? '1 1 1' : '0 0 1';
32
const pathId = `my-pie-chart-path-${index}`;
33
svg.append('path')
34
.attr('id', pathId)
35
.attr('d', `M ${start.x},${start.y} A ${radius},${radius} ${flags} ${end.x},${end.y}`)
36
//.attr('stroke-linecap', 'round')
37
.style('stroke', color)
38
.style('fill', 'none')
39
.style('stroke-width', arcWidth);
40
41
const textBg = svg.append('path');
42
43
const textElement = svg.append('text')
44
.text(text)
45
.attr('dy', 6)
46
.attr('text-anchor', 'middle');
47
48
const textBox = textElement.node().getBBox();
49
textElement.text(null);
50
const bgAngle = Math.abs(textBox.x) / radius;
51
const midAngle = anglePos - angle / 2;
52
const endBgAngle = midAngle + bgAngle;
53
const startBg = {
54
x: center.x + radius * Math.sin(midAngle - bgAngle),
55
y: center.y + radius * -Math.cos(midAngle - bgAngle),
56
};
57
const endBg = {
58
x: center.x + radius * Math.sin(midAngle + bgAngle),
59
y: center.y + radius * -Math.cos(midAngle + bgAngle),
60
};
61
textBg.attr('d', `M ${startBg.x},${startBg.y} A ${radius},${radius} 0 0 1 ${endBg.x},${endBg.y}`)
62
.style('stroke', 'white')
63
.style('fill', 'none')
64
.attr('stroke-linecap', 'round')
65
.style('stroke-width', arcWidth * 0.6);
66
67
textElement.append('textPath')
68
.text(text)
69
.attr('startOffset', angle * radius / 2)
70
.attr('href', `#${pathId}`)
71
});
JavaScript
1
5
1
text {
2
font-family: Calibri;
3
font-size: 16px;
4
fill: black;
5
}
JavaScript
1
3
1
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/6.7.0/d3.min.js"></script>
2
3
<svg width="200" height="200"></svg>