如何更新可重复使用的嵌套甜甜圈图?

时间:2019-05-17 08:39:13

标签: javascript d3.js

我有一个具有数据更新功能的可重用图表-遵循Rob Moore的本文建议:https://www.toptal.com/d3-js/towards-reusable-d3-js-charts-我将其称为 pizza

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

该图表具有两个设置器函数:.data()和.numberOfRings()。

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

dataArray

n 个副本被传递到d3.pie(),以确定每个 n 切片。然后根据它们的索引,为pie返回的对象分配外半径,内半径和其他有用的属性:

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]传递给Pizza的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

1 个答案:

答案 0 :(得分:1)

您根本没有更新现有项目-您的所有操作都是在选择新项目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)))