我正在使用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();
}
奇怪的行为是一次,有些圈子有点“交换”他们的位置。它不会一直发生,但仍然足以引起我的关注。
示例
更具体地说,假设我的数据没有变化(因此它接收到相同的数据),但是在过渡期间,我看到圆B移到了A,而圆A正在移到B。过渡完成后,圆圈B的文本变为“ A”,圆圈A的文本变为“ B”,因此,如果查看结果,则没有任何变化。但我仍然可以看到过渡期间圈子在移动。
希望我的问题解释得足够清楚,如有必要,我可以上传视频。这对我来说真的很奇怪,任何想法都会受到欢迎。预先感谢!
答案 0 :(得分:0)
此处最可能的解释是您认为数据完全相同,但是数据中对象的顺序不同。缺少关键功能的Togheter会带来很大的不同。
在D3中,如果未设置键功能,则根据索引绑定数据。 API解释:
如果未指定键功能,则将数据中的第一个数据分配给第一个选定元素,将第二个数据分配给第二个选定元素,依此类推。
我们可以根据您的代码在这个简单的演示中看到它。在这里,数据几乎相同,但是A
和B
对象在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>