Javascript 对于D3.js,重新绘制或“绘制”是否更好;“移动”;物体?
我一直在尝试动画 通过清除整个画布并在每一帧(或“勾选”)的新位置重新绘制对象,在画布上设置对象动画非常简单: 这是一个坏习惯还是我做得对 例如,如果您正在制作一个满是四处移动的对象的屏幕,那么通过更新它们在每个帧中的属性(例如,x,y坐标)来设置它们的动画是更好的做法吗 或者,也许还有其他我完全不知道的方法,不是吗Javascript 对于D3.js,重新绘制或“绘制”是否更好;“移动”;物体?,javascript,canvas,d3.js,Javascript,Canvas,D3.js,我一直在尝试动画 通过清除整个画布并在每一帧(或“勾选”)的新位置重新绘制对象,在画布上设置对象动画非常简单: 这是一个坏习惯还是我做得对 例如,如果您正在制作一个满是四处移动的对象的屏幕,那么通过更新它们在每个帧中的属性(例如,x,y坐标)来设置它们的动画是更好的做法吗 或者,也许还有其他我完全不知道的方法,不是吗 注意:我的动画一次可能包含100-200个视图中的对象。最好移动它们,因为这是唯一可以无错误地设置动画的方法 在d3.js中,对象是数据绑定的。清除和重画“画布”不是正确的方法。首
注意:我的动画一次可能包含100-200个视图中的对象。最好移动它们,因为这是唯一可以无错误地设置动画的方法 在d3.js中,对象是数据绑定的。清除和重画“画布”不是正确的方法。首先,它不是画布,而是网页,任何清除和重画都由浏览器自己处理。基本上,您的工作是将数据绑定到SVG 您需要使用d3事件,
enter
,exit
,update
,它处理修改数据绑定基础数据时SVG的行为,并让d3处理动画
最简单的例子如下:
var svg=d3.选择(“svg”)代码>
var circles=svg.selectAll('circle')代码>
现在我们需要将一些数据绑定到圆
var-databoundCircles=circles.data([12,13,14,15,66])代码>
这些数据可以是任何东西。通常我希望有一个对象列表,但这些都是简单的数字
当数据出现时,处理事物的“生成”方式
databoundCircles.enter().append('circle')代码>
处理删除数据时发生的情况
databoundCircles.exit().remove()
处理数据更新时发生的情况
databoundCircles.attr('r',函数(d,i){返回d*2;})
这将在数据更改时更改半径
并回顾该教程:
输入-输入元素,进入阶段
更新-持久元素,停留在舞台上
退出-退出元素,退出舞台
总之:不要像现在这样做。确保您专门使用这些事件来处理元素的生命周期
专业提示:如果您使用的是对象列表,请确保通过id
或某些唯一标识符绑定数据,否则动画可能会随着时间的推移表现异常。请记住,您是在将数据绑定到SVG,而不仅仅是在擦除和重画画布
d3.selectAll('circle')。数据([{id:1},{id:2}],函数(d){return d.id;})代码>
请注意可选的第二个参数,它告诉我们如何绑定数据!非常重要
var svg=d3.选择(“svg”);
//数据如下所示。
风险值数据=[{
id:1,
r:3,
x:35,
y:30
}, {
id:2,
r:5,
x:30,
y:35
}];
//数据生成器生成上面的列表
函数newList(){
//只需制作一个包含数字1的简单数组
var items=新数组(randoNum(1,10)).fill(1)
//制作数据片段。ID很重要!
返回项目。映射(函数(val,i){
var r=randoNum(1,16)
返回{
id:我,
r:r,
x:randoNum(1200)+r,
y:randoNum(1100)+r
}
});
}
//我只是用这个来做rando数字。
函数randoNum(从,到){
返回Math.floor(Math.random()*(to-from)+from);
}
功能更新(数据){
//1.获得圆(第一次通过时没有圆!)
var circles=svg.selectAll('circle');
//2.绑定数据
var databoundCircles=circles.data(数据,函数(d){
返回d.id;
});
//3.进入
var enter=databoundCircles.enter()
.append('圆')
.attr('r',0)
//4.退出
databoundCircles.exit()
.transition()
.attr('r',0)
.remove();
//5.更新
//(过渡后的一切都是粗花呢)
数据边界圆
.attr('fill',函数(d,i){
var h=parseInt(i.toString(16));
返回“#”+[h,h,h].join(“”);
})
.transition()
.持续时间(1000)
.attr('r',函数(d,i){
返回d.r*4
})
.attr('cx',函数(d,i){
返回d.x*2;
})
.attr('cy',函数(d,i){
返回d.y*2
})
;
}
//第一次运行时,我使用上面的示例数据
更新(数据);
//现在我每隔几秒钟更新一次
//观察d3如何“跟踪”每个圆圈
setInterval(函数(){
更新(newList());
}, 2000);代码>
这是一个坏习惯还是我做得对
是的,这是个坏习惯。在正常情况下,我喜欢称之为惰性编码:清除SVG(或其他)并再次绘制dataviz
但是,在您的情况下,情况更糟:您最终将编写大量的代码(但不完全是懒惰),忽略了可以轻松完成您想要的任务的d3.transition()
。这就引出了你的第二个问题:
或者,也许还有其他我完全不知道的方法,不是吗
是的,正如我刚才所说,它被称为transition()
:
最后,你说:
注意:我的动画一次可能包含100-200个视图中的对象
首先,现代浏览器可以很好地处理这个问题。其次,您仍然需要手动删除并重新绘制所有这些元素。如果对这两种方法进行基准测试,可能情况会更糟
因此,只需使用d3.transition()
您可以随时更改元素的数据(或属性),并将它们“移动”(或转换)到调用转换的新值。例如
// Inside requestAnimationFrame(...) callback
// Clear canvas
canvas.selectAll('*').remove();
// ... calculate position of x and y
// x, y = ...
// Add object in new position
canvas.append('circle')
.attr('cx', x)
.attr('cy', y)
.attr('r', 10)
.attr('fill', '#ffffff');