添加SVG环形背景后,可变半径饼图首个元素不显示问题排查
可变半径饼图添加环形背景后首个元素消失的解决方法
问题概述
开发可变半径饼图时,添加环形背景后背景显示正常,但图表的首个元素无法显示。
原因分析
问题出在D3.js的选择器逻辑上:添加背景的<path>元素后,执行svg.selectAll('path')会选中这个背景路径,绑定数据时第一个数据会与已有背景路径进行数据匹配(update操作),而非进入enter()阶段创建新路径。由于未对update部分做处理,第一个数据对应的路径被忽略,导致首个元素不显示。
解决方法
有两种简单的修复方式:
方式一:为背景路径添加专属类名,避免被饼图路径选择器选中
创建背景路径时添加类名,后续选择饼图路径时排除该类:
// 创建背景路径时添加类名 var bg = svg.append('path') .attr('class', 'donut-bg') // 新增类名 .attr('d', d3.svg.arc() .innerRadius(innerRadius-10) .outerRadius(innerRadius + donutWidth + 80) .startAngle(0) .endAngle(2 * Math.PI)) .attr('fill', '#18a34a'); // 选择饼图路径时排除背景类 var path = svg.selectAll('path:not(.donut-bg)') .data(data) .enter() .append('path') .attr('d', arc) .attr('fill', function(d){return d.data.colour});
方式二:调整绘制顺序,先创建饼图元素再添加背景
将背景路径的创建代码移到饼图路径和文本之后,确保背景绘制在最底层,同时不干扰饼图路径的选择:
// 先创建饼图路径和文本 var path = svg.selectAll('path') .data(data) .enter() .append('path') .attr('d', arc) .attr('fill', function(d){return d.data.colour}); // ... 文本创建代码 ... // 最后添加背景路径,插入到最前面保证层级在下方 var bg = svg.insert('path', ':first-child') .attr('d', d3.svg.arc() .innerRadius(innerRadius-10) .outerRadius(innerRadius + donutWidth + 80) .startAngle(0) .endAngle(2 * Math.PI)) .attr('fill', '#18a34a');
完整修复后代码(采用方式一)
<!DOCTYPE html> <html> <head> <meta charset="utf-8" /> <title>D3.js Variable Radius Donut Chart</title> <link data-require="normalize@*" data-semver="3.0.1" rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/normalize/3.0.1/normalize.min.css" /> <style> body { background-color: #282a35; } </style> </head> <body> <div id="chart"></div> <script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.6/d3.min.js" data-semver="3.4.6" data-require="d3@*"></script> <script> (function(d3) { 'use strict'; var dataset = [ { count: 3, size: 30, label: 'Use', colour: 'slateblue' }, { count: 1, size: 10, label: 'Production', colour: 'yellow' }, { count: 15, size: 100, label: 'Distribution', colour: 'orange' }, ]; var width = 500; var height = 500; var donutWidth = 50; var innerRadius = (Math.min(width, height) / 4.5) - donutWidth; var svg = d3.select('#chart') .append('svg') .attr('width', width) .attr('height', height) .append('g') .attr('transform', 'translate(' + (width / 2) + ',' + (height / 2) + ')'); var arc = d3.svg.arc() .innerRadius(innerRadius) .outerRadius(function (d) { return innerRadius + d.data.size; }); var pie = d3.layout.pie() .value(function(d) { return d.count; }) .sort(null); var data = pie(dataset); // 添加背景路径并设置类名 var bg = svg.append('path') .attr('class', 'donut-bg') .attr('d', d3.svg.arc() .innerRadius(innerRadius-10) .outerRadius(innerRadius + donutWidth + 80) .startAngle(0) .endAngle(2 * Math.PI)) .attr('fill', '#18a34a'); // 选择非背景类的path元素 var path = svg.selectAll('path:not(.donut-bg)') .data(data) .enter() .append('path') .attr('d', arc) .attr('fill', function(d){return d.data.colour}); var text = svg.selectAll('text') .data(data) .enter() .append('text') .text(function(d) { return d.data.label + ": " + d.data.count; }) .attr('transform', function(d) { var c = arc.centroid(d), x = c[0], y = c[1], h = Math.sqrt(x*x + y*y); return 'translate(' + (x/h * (innerRadius+donutWidth/2)) + ',' + (y/h * (innerRadius+donutWidth/2)) + ')'; }) .attr('dy', '.35em') .attr('fill', 'black') .attr('text-anchor', function(d) { return (d.endAngle + d.startAngle)/2 > Math.PI ? 'end' : 'start'; }); })(window.d3); </script> </body> </html>
内容的提问来源于stack exchange,提问作者GreySkySpin
相关产品推荐
相关产品推荐

