更新基础数据后,D3 forceCollide无法正常工作

时间:2018-12-20 21:23:48

标签: d3.js

我一直在尝试通过阅读一些教程来学习d3,并决定构建一个气泡图,其中包括d3力以用于碰撞以及拖动和动态更新基础数据

基本上,我想要实现的是我通过增加a来每秒更改一次'a'的value属性,并期望'a'的气泡增加并推动周围的其他气泡

const circles = this.svg.selectAll('circle').data(this.state.data);
const node = circles
      .enter()
      .append('g')
      .call(d3.drag()
           .on('start', dragstarted)
           .on('drag', dragged)
           .on('end', dragended))
      .on('mouseover', d => {
        tooltip.html(d.name + ' ' + d.value);
        return tooltip.style('visibility', 'visible')
      })
      .on('mouseout', () => tooltip.style('visibility', 'hidden'));

      node.append('circle')
        .attr('r', d => d.value)
        .style('fill', 'limegreen');

      node.append('text')
        .text(d => d.name);

我的完整尝试是在这里:https://codepen.io/luanped/pen/rojged?editors=0010

运行它时,您会看到前4秒钟几乎是正确的,“ a”会增加并推动其他气泡(但是将鼠标悬停在气泡上,左下方的工具提示将继续显示初始值,而不是新更新的值)

我猜测某种原因我没有正确地进行内部元素(g内的圆)的过渡-从某种意义上说,尽管我对绘制的半径进行了动画处理,但基础数据模型仍然认为它是原始值?因此,碰撞边界和错误的工具提示

任何帮助将不胜感激

谢谢

1 个答案:

答案 0 :(得分:1)

从我所看到的有两个问题:

  1. 模拟的衰减太快了。
  2. 您不是要更新数据数组中的第一项,而是要替换它。

对于其中一个,最简单的解决方案是选择一个比默认值低的alpha衰减率,这与内存不足类似0.0228。您的模拟正在快速衰减:在您完成更新数值之前,它会先冷却下来,尝试使用较低的值:

 .alphaDecay(0.001);

对于两个人来说,这个问题更加微妙。但是,如果您使用console.log(newData),则可以看到未为第一个节点分配xyvxvy等属性数据数组中的其他项目:

enter image description here

这是因为新数据不是强制布局的一部分。并且,当您更新SVG圆圈和文本上的绑定数据时:

const circles = this.svg.selectAll('circle').data(this.state.data);
const labels = this.svg.selectAll('text').data(this.state.data);
...
circles.transition(t).attr('r', d => d.value);
labels.transition(t).text(d => d.name + ' ' + d.value);

您不会更新设置了鼠标悬停事件的g上的数据,因此它永远不会改变。

我们可以一次性更新父gcircletext和力布局上的数据,我们可以通过更新第一个对象来实现数据数组而不是创建新对象:

Rx.Observable.interval(1000)
  .take(10)
  .subscribe(() => {
    const newData = this.state.data;
    newData[0].value += 10;
    this.setState({ data: newData });
  });

之所以可行,是因为在将数据绑定到选择或将节点分配给强制布局时,选择和强制布局都将使用对数据数组中对象的引用(而不是对数据数组本身的引用)。如果更新数据数组中的对象,则会更新力布局的节点和绑定到选区的数据(这在d3力示例中很常见,其中tick函数不会使用.data()更新数据在重新放置元素之前)。但是,如果替换数据数组中的对象,则不会更新强制布局中的引用或对新数据项的选择。您可以使用simulation.nodes()更新力的节点,但您希望保留vxvyxy属性(否则,它们将默认为新值)。

这里是updated pen

对于其他选项,您还可以在每次绘制时重新启动仿真-这将使Alpha衰减保持默认值。

当然,您也不想创建多余的强制布局/工具提示,如注释中所述