I am new to D3js and I have some sort of bug here. When the percentage of data is very low, the path will appear shorter too, logically. But that also “hides” my text which displays the data percentage. Is there any way possible to make my text white or at least make it appear on top of it’s path? Here’s my code snipped. Thank you in advance
var data = [ {"platform": "Yes", "percentage": 87.00}, //skyblue {"platform": "No", "percentage": 1.00}, //darkblue {"platform": "N/A", "percentage": 17.00} //orange ]; var svgWidth = 200, svgHeight = 200, radius = Math.min(svgWidth, svgHeight) / 2; var svg = d3.select('svg') .attr("width", svgWidth) .attr("height", svgHeight); //Create group element to hold pie chart var g = svg.append("g") .attr("transform", "translate(" + radius + "," + radius + ")") ; var color = d3.scaleOrdinal(d3.schemeCategory20); var pie = d3.pie().value(function(d) { return d.percentage; }); var path = d3.arc() .outerRadius(80) .innerRadius(40); var arc = g.selectAll("arc") .data(pie(data)) .enter() .append("g"); arc.append("path") .attr("d", path) .attr("fill", function(d) { return color(d.data.percentage); }); var label = d3.arc() .outerRadius(20) .innerRadius(100); arc.append("text") .attr("transform", function(d) { return "translate(" + label.centroid(d) + ")"; }) .attr("text-anchor", "middle") .text(function(d) { return +d.data.percentage+"%"; });
.cnt-main-container { display:flex; flex-direction: column; } .charts{ display:flex; justify-content: center; flex-direction: row; flex-wrap: nowrap; } .info{ display:flex; justify-content: center; flex-direction: row; } #dot1{ width:10px; height:10px; background:rgb(39, 81, 194); display:inline-block; } label{ margin-left:3px; margin-top:-3px; margin-right:5px; } #dot2{ width:10px; height:10px; background:rgb(233, 106, 3); display:inline-block; } #dot3{ background-color:skyblue; width:10px; height:10px; display:inline-block; }
<div class="cnt-main-container"> <div class="charts"> <svg class="pie-chart"></svg> </div> <div class="info"> <div id="dot1"></div> <label for="dot1">Yes</label> <div id="dot2"></div> <label for="dot2">No</label> <div id="dot3"></div> <label for="dot3">N/A</label> </div> </div> <script src="https://d3js.org/d3.v4.min.js"></script> <script src="script.js"></script>
Advertisement
Answer
In a SVG the order of the elements defines the painting order. Thus, the simples solution is sorting your enter selection i such a way that small slices will be on the top:
.sort((a, b) => b.data.percentage - a.data.percentage);
Also, you can set a small white stroke to the texts, if the browser supports paint-order
.
Here is the result:
var data = [{ "platform": "Yes", "percentage": 87.00 }, //skyblue { "platform": "No", "percentage": 1.00 }, //darkblue { "platform": "N/A", "percentage": 17.00 } //orange ]; var svgWidth = 200, svgHeight = 200, radius = Math.min(svgWidth, svgHeight) / 2; var svg = d3.select('svg') .attr("width", svgWidth) .attr("height", svgHeight); //Create group element to hold pie chart var g = svg.append("g") .attr("transform", "translate(" + radius + "," + radius + ")"); var color = d3.scaleOrdinal(d3.schemeCategory20); var pie = d3.pie().value(function(d) { return d.percentage; }); var path = d3.arc() .outerRadius(80) .innerRadius(40); var arc = g.selectAll("arc") .data(pie(data)) .enter() .append("g") .sort((a, b) => b.data.percentage - a.data.percentage); arc.append("path") .attr("d", path) .attr("fill", function(d) { return color(d.data.percentage); }); var label = d3.arc() .outerRadius(20) .innerRadius(100); arc.append("text") .attr("transform", function(d) { return "translate(" + label.centroid(d) + ")"; }) .attr("text-anchor", "middle") .text(function(d) { return +d.data.percentage + "%"; });
.cnt-main-container { display: flex; flex-direction: column; } .charts { display: flex; justify-content: center; flex-direction: row; flex-wrap: nowrap; } .info { display: flex; justify-content: center; flex-direction: row; } #dot1 { width: 10px; height: 10px; background: rgb(39, 81, 194); display: inline-block; } label { margin-left: 3px; margin-top: -3px; margin-right: 5px; } #dot2 { width: 10px; height: 10px; background: rgb(233, 106, 3); display: inline-block; } #dot3 { background-color: skyblue; width: 10px; height: 10px; display: inline-block; } text { stroke: white; stroke-width: 2px; paint-order: stroke; }
<div class="cnt-main-container"> <div class="charts"> <svg class="pie-chart"></svg> </div> <div class="info"> <div id="dot1"></div> <label for="dot1">Yes</label> <div id="dot2"></div> <label for="dot2">No</label> <div id="dot3"></div> <label for="dot3">N/A</label> </div> </div> <script src="https://d3js.org/d3.v4.min.js"></script> <script src="script.js"></script>