d3.transition的奇怪行为

时间:2019-07-15 19:19:36

标签: javascript reactjs d3.js

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

核心概念是我每隔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();
}

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

示例

enter image description here

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

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

1 个答案:

答案 0 :(得分:0)

此处最可能的解释是您认为数据完全相同,但是数据中对象的顺序不同。缺少关键功能的Togheter会带来很大的不同。

在D3中,如果未设置键功能,则根据索引绑定数据。 API解释:

  

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

我们可以根据您的代码在这个简单的演示中看到它。在这里,数据几乎相同,但是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>