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

如何在D3.js饼图中点击切片后替换为子切片并实现平滑过渡

如何在D3.js饼图中点击切片后替换为子切片并实现平滑过渡

我完全理解你想要的效果——点击饼图里的某个父切片后,用它的子切片替换掉它,同时让其他父切片平滑收缩消失,子切片慢慢展开填满整个饼图,而不是生硬地重绘整个图表。下面我来一步步帮你实现这个交互效果:

核心思路

我们需要分两步处理:

  1. 先创建一个过渡用的临时数据集:把非目标父切片的数值设为0(让它们收缩消失),同时把目标父切片替换成它的子切片(让子切片从原位置展开)。
  2. 利用D3的过渡动画功能,让饼图从当前状态平滑过渡到临时数据集的状态,动画结束后再把正式数据集替换为子切片数据,方便后续交互。

修改后的完整代码

下面是调整后的JavaScript代码,关键部分我都加了注释:

// 初始父切片数据
var parents = [
  { 'name': 'parent1', 'value': 50 },
  { 'name': 'parent2', 'value': 20 },
  { 'name': 'parent3', 'value': 80 },
  { 'name': 'parent4', 'value': 40 },
]

// parent1的子切片数据
var children = [
  { 'name': 'children1', 'value': 10 },
  { 'name': 'children2', 'value': 5 },
  { 'name': 'children3', 'value': 20 },
  { 'name': 'children4', 'value': 15 },
]

var data = parents;
data.sort((a, b) => b.value - a.value);

var container = document.getElementById('chart');

const width = container.offsetWidth;
const height = width;
const outer = height / 2;
const inner = outer * .5;
const radius = width / 4;
const tau = 2 * Math.PI;

const color1 = d3.color("hsl(0, 45%, 55%)");
const color2 = d3.color("hsl(360, 45%, 55%)");
const interpolation = d3.interpolateHslLong(color1, color2);

var color = d3.scaleOrdinal(d3.quantize(interpolation, data.length + 1));
var svg = d3.create("svg").attr("viewBox", [-width/2, -height/2, width, height]);
var arc = d3.arc().innerRadius(inner).outerRadius(outer);

// 创建饼图生成器
var pie = d3.pie().value((d) => d["value"]);

const path = svg.datum(data).selectAll("path")
  .data(pie)
  .join("path")
  .attr("fill", (d, i) => color(i))
  .attr("d", arc)
  .style("cursor", "pointer")
  .each(function(d) { this._current = d; }) // 保存当前状态,用于过渡
  .on("click", (e, d) => {
    if (d.data.name === 'parent1') {
      // 1. 准备过渡用的临时数据:其他父元素收缩到0,替换parent1为子元素
      const transitionData = parents
        .map(item => item.name !== 'parent1' ? {...item, value: 0} : null)
        .filter(item => item !== null)
        .concat(children);

      // 2. 更新颜色比例尺,适配子元素数量
      color = d3.scaleOrdinal(d3.quantize(interpolation, children.length + 1));

      // 3. 执行平滑过渡动画
      path.data(pie(transitionData))
        .transition()
        .duration(750) // 动画时长,可根据需求调整
        .attrTween("d", function(nextData) {
          const currentData = this._current;
          // 插值计算过渡过程中的每个状态
          const interpolate = d3.interpolate(currentData, nextData);
          // 更新当前状态为初始过渡值
          this._current = interpolate(0);
          // 返回每个时间点的路径生成函数
          return function(t) {
            return arc(interpolate(t));
          };
        })
        .on("end", () => {
          // 动画结束后,正式切换数据集为子元素
          data = children;
          // 更新饼图的最终状态,确保后续交互正常
          path.data(pie(data))
            .attr("fill", (d, i) => color(i))
            .each(function(d) { this._current = d; });
        });
    }
  })

container.append(svg.node());

关键细节解释

  • 过渡数据处理:通过把非目标父元素的value设为0,让它们在动画中逐渐收缩消失;同时将目标父元素替换为子元素,子元素会从原父元素的位置平滑展开。
  • attrTween方法:这是实现饼图形状平滑过渡的核心,它允许我们在过渡过程中逐帧计算路径的变化,避免生硬的形状跳转。
  • 状态保存与更新:通过this._current保存每个切片的当前状态,确保过渡动画能基于当前位置开始,而不是从头开始。
  • 动画结束后的收尾:动画完成后切换正式数据集为子元素,并更新切片的颜色和状态,保证后续如果子元素还有子节点(需要进一步展开),交互能正常工作。

扩展建议

如果需要支持返回父级的功能,可以维护一个数据栈:每次进入子级时把当前父级数据压入栈,添加一个返回按钮,点击时弹出栈顶数据并执行反向过渡动画即可。

备注:内容来源于stack exchange,提问作者BFlat

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.15 03:20:16