【问题标题】:Odd behavior of d3.transitiond3.transition 的奇怪行为
【发布时间】:2019-11-24 12:33:19
【问题描述】:

我正在使用 react 和 d3js 开发个人网络应用程序。

核心概念是我每 5 秒通过 websocket 接收一堆数据,并使用这些数据在画布上实时绘制圆圈。我收到的数据是一个对象数组,如下所示:

data = [{ name: 'alice', x: 10, y: 20, }...]

这就是我画圆圈的方式:

_renderCircles = () => {
    const t = d3.transition().duration(500);
    const radius = 3;

    const group = d3.select(this.refs.container)
                    .selectAll('g.ring')
                    .data(data);
    const circleGroup = group.enter().append('g')
                    .attr('class', 'cart')
                    .attr('transform', d => {
                        return `translate(${d.x}, ${d.y})`;
                    });
    circleGroup.append('circle');
    circleGroup.append('text')
                    .attr('text-anchor', 'middle')
                    .attr('fill', 'white');

    group.select('circle')
                    .attr('r', radius)
                    .style('fill', 'yellow');
    group.select('text')
                    .text(d => { return d.name; });

    group.transition(t)
                    .attr('transform', d => {
                        return `translate(${d.x}, ${d.y})`;
                    };

    group.exit().remove();
}

奇怪的行为是有时,一些圈子会“交换”他们的位置。它不会一直发生,但仍然经常引起我的关注。

例子

更具体地说,假设我的数据没有改变(所以它接收到相同的数据),但是在过渡期间,我可以看到圆圈 B 正在移动到 A,而圆圈 A正在移动到 B。过渡完成后,圆圈 B 的文本变为“A”,圆圈 A 的文本变为“B”,所以如果我们查看结果,没有任何变化。但我仍然可以看到在过渡期间圆圈在移动。

我希望我的问题解释得足够清楚,如有必要,我可以上传视频。这对我来说真的很奇怪,任何想法都会受到欢迎。提前致谢!

【问题讨论】:

    标签: javascript reactjs d3.js


    【解决方案1】:

    这里最可能的解释是你认为数据完全相同,但是数据中对象的顺序不同。这一点,再加上缺少关键功能,就完全不同了。

    在D3中,如果不设置key函数,数据是按照索引绑定的。 API 解释:

    如果未指定键功能,则将 data 中的第一个数据分配给第一个选定元素,将第二个数据分配给第二个选定元素,依此类推。

    我们可以根据您的代码在这个简单的演示中看到它。这里,数据几乎一样,只是AB对象在newData数组中互换了:

    const data = [{
      name: "A",
      x: 20,
      y: 20
    }, {
      name: "B",
      x: 20,
      y: 120
    }, {
      name: "C",
      x: 100,
      y: 40
    }, {
      name: "D",
      x: 260,
      y: 20
    }, {
      name: "E",
      x: 210,
      y: 130
    }];
    const newData = [{
      name: "B",
      x: 20,
      y: 120
    }, {
      name: "A",
      x: 20,
      y: 20
    }, {
      name: "C",
      x: 100,
      y: 40
    }, {
      name: "D",
      x: 260,
      y: 20
    }, {
      name: "E",
      x: 210,
      y: 130
    }];
    const svg = d3.select("svg");
    
    renderCircles(data);
    d3.timeout(function() {
      renderCircles(newData);
    }, 1000)
    
    function renderCircles(data) {
      const t = d3.transition().duration(1000);
      const radius = 12;
    
      const group = svg.selectAll('g')
        .data(data);
      const circleGroup = group.enter().append('g')
        .attr('class', 'cart')
        .attr('transform', d => {
          return `translate(${d.x}, ${d.y})`;
        });
      circleGroup.append('circle')
        .attr('r', radius)
        .style('fill', 'tan');
      circleGroup.append('text')
        .attr('text-anchor', 'middle')
        .attr("dy", 4)
        .text(d => {
          return d.name;
        });
    
      group.select('text')
        .text(d => {
          return d.name;
        });
    
      group.transition(t)
        .attr('transform', d => {
          return `translate(${d.x}, ${d.y})`;
        });
    
      group.exit().remove();
    }
    <script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
    <svg></svg>

    因此,解决方案是使用按键功能。例如:

    .data(data, d => d.name);
    

    现在看一下相同的代码,只有那个区别......当我们用新数据调用函数时什么都不会发生:

    const data = [{
      name: "A",
      x: 20,
      y: 20
    }, {
      name: "B",
      x: 20,
      y: 120
    }, {
      name: "C",
      x: 100,
      y: 40
    }, {
      name: "D",
      x: 260,
      y: 20
    }, {
      name: "E",
      x: 210,
      y: 130
    }];
    const newData = [{
      name: "B",
      x: 20,
      y: 120
    }, {
      name: "A",
      x: 20,
      y: 20
    }, {
      name: "C",
      x: 100,
      y: 40
    }, {
      name: "D",
      x: 260,
      y: 20
    }, {
      name: "E",
      x: 210,
      y: 130
    }];
    const svg = d3.select("svg");
    
    renderCircles(data);
    d3.timeout(function() {
      renderCircles(newData);
    }, 1000)
    
    function renderCircles(data) {
      const t = d3.transition().duration(1000);
      const radius = 12;
    
      const group = svg.selectAll('g')
        .data(data, d => d.name);
      const circleGroup = group.enter().append('g')
        .attr('class', 'cart')
        .attr('transform', d => {
          return `translate(${d.x}, ${d.y})`;
        });
      circleGroup.append('circle')
        .attr('r', radius)
        .style('fill', 'tan');
      circleGroup.append('text')
        .attr('text-anchor', 'middle')
        .attr("dy", 4)
        .text(d => {
          return d.name;
        });
    
      group.select('text')
        .text(d => {
          return d.name;
        });
    
      group.transition(t)
        .attr('transform', d => {
          return `translate(${d.x}, ${d.y})`;
        });
    
      group.exit().remove();
    }
    <script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
    <svg></svg>

    【讨论】:

    • 救命稻草!!非常感谢,是的,这是因为数据顺序不同。 :thumbup
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-02-09
    • 1970-01-01
    • 1970-01-01
    • 2023-03-20
    相关资源
    最近更新 更多