You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

添加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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.26 18:23:14