我对d3.js还不熟悉,我知道有两种绘制对象的方法——SVG和Canvas。 我的使用场景是涉及<100个节点和边缘。我已经尝试过使用Canvas的几个例子,效果很好。
我看到SO上有一个关于SVG和Canvas之间区别的帖子。
对我来说,这两者都可以,但是我更倾向于使用Canvas(因为我已经有了一些可行的例子)。如果我在d3.js上下文中漏掉了什么,请指出来。
我对d3.js还不熟悉,我知道有两种绘制对象的方法——SVG和Canvas。 我的使用场景是涉及<100个节点和边缘。我已经尝试过使用Canvas的几个例子,效果很好。
我看到SO上有一个关于SVG和Canvas之间区别的帖子。
对我来说,这两者都可以,但是我更倾向于使用Canvas(因为我已经有了一些可行的例子)。如果我在d3.js上下文中漏掉了什么,请指出来。
data.forEach(function(d) {
// drawing instructions:
context.beginPath()....
})
// container for dummy elements:
var faux = d3.select(document.createElement("custom"));
然后我们可以根据需要进行选择,使用enter/exit/update/append/remove/transition等方法:
// treat as any other DOM elements:
var bars = faux.selectAll(".bar").data(data).enter()....
但是由于这些选择中的元素没有被渲染,我们需要指定如何以及何时绘制它们。没有数据绑定和画布,我们根据数据直接绘制元素,有了数据绑定和画布,我们基于虚拟DOM中的选择/元素进行绘制:
bars.each(function() {
var selection = d3.select(this);
context.beginPath();
context.fillRect(selection.attr("x"), selection.attr("y")...
...
})
我们可以在退出/进入/更新等情况下重新绘制元素,这可能具有一些优势。这还允许D3过渡,通过在虚拟元素上连续重绘而在过渡属性时进行转换。
以下示例具有完整的输入/输出/更新循环和过渡,演示了带有数据绑定的画布:
var canvas = d3.select("body")
.append("canvas")
.attr("width", 600)
.attr("height", 200);
var context = canvas.node().getContext("2d");
var data = [1,2,3,4,5];
// container for dummy elements:
var faux = d3.select(document.createElement("custom"));
// normal update exit selection with dummy elements:
function update() {
// modify data:
manipulateData();
var selection = faux.selectAll("circle")
.data(data, function(d) { return d;});
var exiting = selection.exit().size();
var exit = selection.exit()
.transition()
.attr("r",0)
.attr("cy", 70)
.attr("fill","white")
.duration(1200)
.remove();
var enter = selection.enter()
.append("circle")
.attr("cx", function(d,i) {
return (i + exiting) * 20 + 20;
})
.attr("cy", 50)
.attr("r", 0)
.attr("fill",function(d) { return ["orange","steelblue","crimson","violet","yellow"][d%5]; });
enter.transition()
.attr("r", 8)
.attr("cx", function(d,i) {
return i * 20 + 20;
})
.duration(1200);
selection
.transition()
.attr("cx", function(d,i) {
return i * 20 + 20;
})
.duration(1200);
}
// update every 1.3 seconds
setInterval(update,1300);
// rendering function, called repeatedly:
function render() {
context.clearRect(0, 0, 600, 200);
faux.selectAll("circle").each(function() {
var sel = d3.select(this);
context.beginPath();
context.arc(sel.attr("cx"),sel.attr("cy"),sel.attr("r"),0,2*Math.PI);
context.fillStyle = sel.attr("fill");
context.fill();
context.stroke();
})
window.requestAnimationFrame(render)
}
window.requestAnimationFrame(render)
// to manipulate data:
var index = 6; // to keep track of elements.
function manipulateData() {
data.forEach(function(d,i) {
var r = Math.random();
if (r < 0.5 && data.length > 1) {
data.splice(i,1);
}
else {
data.push(index++);
}
})
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.10.0/d3.min.js"></script>
块版本。
摘要
使用canvas,数据绑定需要一组虚拟元素,但是一旦绑定,您可以轻松使用过渡和更新/输入/退出循环。但是,渲染与更新/输入/退出和转换分离-由您决定何时以及如何重新绘制可视化。这个绘图发生在更新/输入/退出和转换方法之外。
使用svg,输入/更新/退出循环和转换会更新可视化中的元素,将渲染和数据链接在一步中。
在canvas中,使用伪元素进行数据绑定,可视化表示伪节点。在svg中,可视化是节点。
数据绑定是一个基本差异,惯用的D3在SVG中需要它,但是在使用Canvas时可以选择是否使用它。但是下面提到了Canvas和SVG之间的其他差异:
也许使用Canvas时最重要的问题是它是无状态的,只是像素的集合而不是元素。这使得当与特定渲染形状交互时,鼠标事件变得困难。虽然鼠标可以与Canvas交互,但标准事件仅对特定像素的交互触发。
因此,虽然在SVG中,我们可以为力导向图中的每个节点分配一个点击监听器(例如),但是在Canvas中,我们为整个画布设置一个点击监听器,然后根据位置确定应该认为哪个节点被“点击”。
上述提到的 D3-force 画布 示例 使用了力导向图的.find
方法,用它来查找最接近鼠标点击的节点,然后将拖动主题设置为该节点。可见画布中的每个形状都绘制在不可见画布上,但在不可见画布上,它具有唯一的颜色。在可见画布上获取鼠标事件的 xy 坐标,我们可以使用相同的 xy 在不可见画布上获取像素颜色。由于 HTML 中颜色是数字,因此我们可以将该颜色转换为数据的索引。
反转比例尺(缩放的 xy 位置到未缩放的输入值)用于热图/网格化数据(示例)
使用未渲染的 Voronoi 图的 .find
方法查找最靠近事件的节点(对于点、圆)
.find
方法查找最靠近事件的节点(对于点、圆,主要在力导向图的上下文中使用)