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
import DonutChart from 'react-d3-donut'; let data = [{ count: 20 , color: "red" , name: 'My name' },{ count:30, color:"green", name:"yeys" },{ count:20, color:"orange", name:"yeys" }] <DonutChart innerRadius={90} outerRadius={100} transition={true} svgClass="example1" pieClass="pie1" displayTooltip={true} strokeWidth={3} data={data} />
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script> <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:
const data = [ {value: 20, text: 'First', color: 'red'}, {value: 30, text: 'Second', color: 'green'}, {value: 60, text: 'Third', color: 'blue'}, ]; const svg = d3.select('svg'); const width = parseInt(svg.attr('width')); const height = parseInt(svg.attr('height')); const margin = 10; const arcWidth = 30; const radius = Math.min(width/2 - margin, height/2 - margin) - arcWidth / 2; const center = {x: width / 2, y: height / 2}; let anglePos = 0; const angleOffset = 0.025; const sum = data.reduce((s, {value}) => s + value, 0); data.forEach(({value, text, color}, index) => { const angle = Math.PI * 2 * value / sum; const start = { x: center.x + radius * Math.sin(anglePos + angleOffset), y: center.y + radius * -Math.cos(anglePos + angleOffset), }; anglePos += angle; const end = { x: center.x + radius * Math.sin(anglePos - angleOffset), y: center.y + radius * -Math.cos(anglePos -angleOffset), }; const flags = value / sum >= 0.5 ? '1 1 1' : '0 0 1'; const pathId = `my-pie-chart-path-${index}`; svg.append('path') .attr('id', pathId) .attr('d', `M ${start.x},${start.y} A ${radius},${radius} ${flags} ${end.x},${end.y}`) .style('stroke', color) .style('fill', 'none') .style('stroke-width', arcWidth); svg.append('text') .attr('dy', 6) .attr('text-anchor', 'middle') .append('textPath') .attr('startOffset', angle * radius / 2) .attr('href', `#${pathId}`) .text(text) });
text { font-family: Calibri; font-size: 20px; font-weight: bold; fill: white; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/6.7.0/d3.min.js"></script> <svg width="200" height="200"></svg>
An example with text backgrounds:
const data = [ {value: 20, text: 'First', color: 'red'}, {value: 30, text: 'Second', color: 'green'}, {value: 60, text: 'Third', color: 'blue'}, ]; const svg = d3.select('svg'); const width = parseInt(svg.attr('width')); const height = parseInt(svg.attr('height')); const margin = 10; const arcWidth = 30; const radius = Math.min(width/2 - margin, height/2 - margin) - arcWidth / 2; const center = {x: width / 2, y: height / 2}; let anglePos = 0; const angleOffset = 0.025; const sum = data.reduce((s, {value}) => s + value, 0); data.forEach(({value, text, color}, index) => { const angle = Math.PI * 2 * value / sum; const start = { x: center.x + radius * Math.sin(anglePos + angleOffset), y: center.y + radius * -Math.cos(anglePos + angleOffset), }; anglePos += angle; const end = { x: center.x + radius * Math.sin(anglePos - angleOffset), y: center.y + radius * -Math.cos(anglePos -angleOffset), }; const flags = value / sum >= 0.5 ? '1 1 1' : '0 0 1'; const pathId = `my-pie-chart-path-${index}`; svg.append('path') .attr('id', pathId) .attr('d', `M ${start.x},${start.y} A ${radius},${radius} ${flags} ${end.x},${end.y}`) //.attr('stroke-linecap', 'round') .style('stroke', color) .style('fill', 'none') .style('stroke-width', arcWidth); const textBg = svg.append('path'); const textElement = svg.append('text') .text(text) .attr('dy', 6) .attr('text-anchor', 'middle'); const textBox = textElement.node().getBBox(); textElement.text(null); const bgAngle = Math.abs(textBox.x) / radius; const midAngle = anglePos - angle / 2; const endBgAngle = midAngle + bgAngle; const startBg = { x: center.x + radius * Math.sin(midAngle - bgAngle), y: center.y + radius * -Math.cos(midAngle - bgAngle), }; const endBg = { x: center.x + radius * Math.sin(midAngle + bgAngle), y: center.y + radius * -Math.cos(midAngle + bgAngle), }; textBg.attr('d', `M ${startBg.x},${startBg.y} A ${radius},${radius} 0 0 1 ${endBg.x},${endBg.y}`) .style('stroke', 'white') .style('fill', 'none') .attr('stroke-linecap', 'round') .style('stroke-width', arcWidth * 0.6); textElement.append('textPath') .text(text) .attr('startOffset', angle * radius / 2) .attr('href', `#${pathId}`) });
text { font-family: Calibri; font-size: 16px; fill: black; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/6.7.0/d3.min.js"></script> <svg width="200" height="200"></svg>