首页 > 解决方案 > 如何更新可重复使用的嵌套圆环图?

问题描述

我有一个具有数据更新功能的可重复使用图表——遵循 Rob Moore 的这篇文章的建议:https: //www.toptal.com/d3-js/towards-reusable-d3-js-charts——我是叫披萨

Pizza 是一个嵌套的甜甜圈图。它看起来像一个带有同心圆环的饼图。我想添加一个交互,用户可以点击饼图的一个切片,所选切片将展开以填充整个饼图。

该图表有两个 setter 函数:.data() 和 .numberOfRings()。

Pizza.data( dataArray ).numberOfRings( n ) 生成具有n 个同心环和dataArray.length多个切片的图表,其中dataArray是整数数组。

n个dataArray副本被传递给 d3.pie() 以确定n 个切片中每个最终弧的 startAngle 和 endAngle 。然后 pie 返回的对象根据它们的索引分配有 outerRadius、innerRadius 和其他一些有用的属性:

const ringSet = [...Array(numberOfRings).keys()].reverse()

const sliceGenorator = Object.fromEntries(ringSet.map((x, i) => [i, 
data]))

const pie = d3.pie().sort(null)
const arc = d3.arc()

let arcs = d3.values(sliceGenorator).map((d, i) => 
                              pie(d).map((x, v) => 
        ({
            ...x,
            arcSlice: 'slice_' + v,
            arcRing: 'ring_' + i,
            innerRadius: cWidth * i,
            outerRadius: cWidth * (i + 1),
            id: 'ring' + i + '_' + 'slice' + v
        })));

然后将这些对象传递给弧生成器并附加到 svg 组。

const flatArcs = arcs.flat()
const paths = svg.selectAll('path.arc')
    .data(flatArcs)
    .enter()
            .append('path')
    .attr('class', 'arc')
            .attr('d',d => arc(d))
    .attr('id', d => d.id)
    .attr('fill', d => function that colors slices)

我的想法是用索引数组来更新图表数据,如下图:

let data = [30, 22, 8, 10]

//create an instance of the chart. call it 'pizza'
const pizza = pizzaChart()
  .numberOfRings(5)
  .data(data); 

//render the pizza into the div
d3.select('#container')
  .call(pizza)

//returns an index array
function selected(d) {
  let zeros = new Array(data.length).fill(0);
  zeros[d.index] = 1;
    return zeros}

//pass an index array to pizza as the data update
d3.selectAll('path.arc')
  .on('click', d => pizza.data(selected(d)))

假设用户选择切片 1,然后将indexArray = [0, 1, 0, 0] 传递给比萨饼的 updateData 函数,该函数运行与上述相同的过程,生成一个新的弧形对象数组,其起始角度和结束角度已被计算从indexArray中的值。

然后路径以弧 id 作为键更新图表,以证明对象的恒定性。

// update the data and pass in the arc id's as a key
const updatePaths = d3.selectAll('path.arc')
             .data(updateArcs.flat(), d => d.id)

updatePaths.enter()
     .append('path')
     .attr('class', 'arc')
     .attr('d',d => arc(d))
     .attr('stroke', '#334646')
     .attr('id', d => d.id)
 .attr('fill', d => function to color code by slice)

updatePaths.exit().remove();

我希望所选切片中的弧将使用 startAngle = 0 和 endAngle = 2*PI 重新渲染,而其他弧将使用 StartAngle = 0 和 endAngle = 0 重新渲染。因此所选切片将填充图表,而其他切片将折叠。但什么也没有发生。

我最终会添加一个动画过渡,但我只想在我从图表中得到预期的行为后才这样做。

如果我console.log(updatePaths.data())看到预期的数据。

我在错误的轨道上吗?我错过了一些明显的东西吗?

任何帮助将不胜感激

这是我的完整代码的链接:

https://bl.ocks.org/AveryBurke/d366090ccc19c41c8ee7be2958b3f425/46540a46380d7f7eb8ced458e6eda06c2c05f2d2

标签: javascriptd3.js

解决方案


您根本没有更新现有项目 - 您的所有操作都是在选择新项目时进行的updatePaths.enter(),而在任何操作时都没有updatePaths

我添加了一个合并,它开始做我认为你想要的事情

updatePaths.enter()
    .append('path')
    //actions for only new items; things that never change
    .attr('class', 'arc')
    .attr('stroke', '#334646')
    .attr('id', d => d.id)
.merge(updatePaths)
    //actions for both new and existing items
    .attr('d',d => arc(d))
    .attr('fill', d => sliceColor.range(pallet[d.index])(arcRingValue(d)))

推荐阅读