dv.js中svg和canvas之间的区别 [英] Difference between svg and canvas in d3.js

查看:94
本文介绍了dv.js中svg和canvas之间的区别的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我是d3.js的新手.我发现有两种方法可以绘制对象-SVG和Canvas. 我的用例大约是< 100个节点和边缘.我已经尝试了一些使用画布的示例,它看起来很棒.

I am new to d3.js. I figured out there are two ways to get the objects drawn - SVG and Canvas. My use case is around <100 nodes and edges. I have already tried few examples using canvas and it looks great.

我看到在之间的区别SVG和Canvas .

这两种方法都可以使用,但是,我倾向于使用画布(因为我已经很少使用示例了).请纠正我如果我在d3.js上下文中缺少任何内容吗?

Both seem ok for my use, however, I am inclined towards canvas (as I have already few example working). please correct me if I am missing anything in d3.js context?

推荐答案

链接的问题中列出的差异说明了svg和画布(矢量/光栅等)之间的一般差异.但是,对于d3,这些差异还具有其他含义,尤其是考虑到d3的核心部分是数据绑定.

The differences listed in the linked question/answers speak to the general differences between svg and canvas (vector/raster, etc). However, with d3 these differences have additional implications, especially considering that a core part of d3 is data binding.

也许d3的最主要特征是数据绑定. Mike Bostock指出,一旦将数据添加到元素中,他就需要创建d3:

Perhaps the most central feature of d3 is data binding. Mike Bostock states he needed to create d3 once he joined data to elements:

关键时刻是我第一次进行数据联接时 时间.太神奇了.我什至不确定自己是否了解它的工作原理,但是 使用起来很爆炸.我意识到可能有一个实用的工具 可视化不必要地限制了 您可以进行的可视化. 链接

The defining moment was when I got the data-join working for the first time. It was magic. I wasn’t even sure I understood how it worked, but it was a blast to use. I realized there could be a practical tool for visualization that didn’t needlessly restrict the types of visualizations you could make. link

使用SVG,数据绑定很容易-我们可以为单个svg元素分配一个基准,然后使用该基准设置其属性/对其进行更新/等.这是基于svg的状态性-我们可以重新选择一个圆并对其进行修改或访问其属性.

With SVG, data binding is easy - we can assign a datum to an individual svg element and then use that datum to set its attributes/update it/etc. This is built upon the statefulness of svg - we can re-select a circle and modify it or access its properties.

使用Canvas,canvas是无状态的,因此我们无法将数据绑定到Canvas中的形状,因为Canvas仅包含像素.因此,我们无法选择和更新画布中的元素,因为画布没有任何元素可供选择.

With Canvas, canvas is stateless, so we can't bind data to shapes within the canvas as the canvas only comprises of pixels. As such we can't select and update elements within the canvas because the canvas doesn't have any elements to select.

基于上述内容,我们可以看到惯用D3中的svg需要输入/更新/退出循环(或基本的附加语句):我们需要输入元素才能看到它们,并经常根据其基准对它们进行样式设置.使用canvas时,我们不需要 输入任何内容,就像退出/更新一样.没有要追加的元素才能看到,因此我们可以绘制可视化视图,而无需使用d3 svg可视化视图中的enter/update/exit或添加/插入方法(如果需要的话, ).

Based on the above, we can see that the enter/update/exit cycle (or basic append statements) are needed for svg in idiomatic D3: we need to enter elements to see them and we style them often based on their datum. With canvas, we don't need to enter anything, same with exiting/updating. There are no elements to append in order to see, so we can draw visualizations without the enter/update/exit or the append/insert approaches used in d3 svg visualizations, if we want.

没有数据绑定的画布

我将在最后一个问题此处中使用示例bl.ock.因为我们根本不需要附加元素(或向其中附加数据),所以我们使用forEach循环绘制每个功能(这与SVG的惯用D3相反).由于没有要更新的元素,因此我们必须在每个刻度上重绘每个功能-重绘整个框架(注意在每个刻度上清除画布).关于拖动,d3.drag和d3.force具有一些预期可与画布一起使用的功能,并且可以允许我们通过拖动事件直接修改数据数组-绕开了DOM中对节点元素进行直接与鼠标交互的任何需要(d3 .force还直接修改了数据数组-但它也在 svg示例中进行了修改)

I'll use the example bl.ock in your last question, here. Because we don't need to append elements at all (or append data to them), we use a forEach loop to draw each feature (which is counter to idiomatic D3 with SVG). Since there are no elements to update, we have to redraw each feature each tick - redrawing the entire frame (notice the clearing of the canvas each tick). Regarding the drag, d3.drag and d3.force has some functionality anticipating use with canvas, and can allow us to modify the data array directly through drag events - bypassing any need for node elements in the DOM to directly interact with the mouse (d3.force is also modifying the data array directly - but it does this in the svg example as well).

在没有数据绑定的情况下,我们直接根据数据绘制元素:

Without data binding we draw elements based on the data directly:

data.forEach(function(d) {
    // drawing instructions:
    context.beginPath()....
})

如果数据更改,我们可能会重绘数据.

If the data changes, we will probably redraw the data.

具有数据绑定的画布

也就是说,您可以使用canvas实现数据绑定,但是它需要使用伪元素的另一种方法.我们经历了常规的更新/退出/输入周期,但由于我们使用的是虚拟元素,因此不会呈现任何内容.我们可以根据需要重新渲染画布(如果使用过渡,则可以连续渲染),并基于虚拟元素绘制事物.

That said, you can implement data binding with canvas, but it requires a different approach using dummy elements. We go through the regular update/exit/enter cycle, but as we are using dummy elements, nothing is rendered. We re-render the canvas whenever we want (it may be continuously if we are using transitions), and draw things based on the dummy elements.

要创建一个虚拟父容器,我们可以使用:

To make a dummy parent container we can use:

// container for dummy elements:
var faux = d3.select(document.createElement("custom"));

然后,我们可以根据需要使用Enter/exit/update/append/remove/transition/etc等进行选择:

Then we can make selections as needed, using enter/exit/update/append/remove/transition/etc:

// treat as any other DOM elements:
var bars = faux.selectAll(".bar").data(data).enter()....

但是由于未渲染这些选择中的元素,因此我们需要指定如何以及何时绘制它们.在没有数据绑定和Canvas的情况下,我们直接基于数据绘制元素,而在数据绑定和Canvas的情况下,我们基于人造DOM中的选择/元素绘制:

But as elements in these selections aren't rendered, we need to specify how and when to draw them. Without data binding and Canvas we drew elements based on the data directly, with data binding and Canvas we draw based on the selection/element in the faux DOM:

bars.each(function() {
  var selection = d3.select(this);
  context.beginPath();
  context.fillRect(selection.attr("x"), selection.attr("y")...
  ...
})

在这里,只要我们退出/进入/更新等,我们就可以重绘元素,这可能会有一些优势.这还允许通过在仿制元素上转换属性时连续重绘来进行D3转换.

Here we can redraw the elements whenever we exit/enter/update etc which may have some advantages. This also allows D3 transitions by redrawing continuously while transitioning properties on the faux elements.

下面的示例具有一个完整的输入/退出/更新周期,其中包含过渡,展示了具有数据绑定功能的画布:

The below example has a complete enter/exit/update cycle with transitions, demonstrating canvas with data binding:

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,数据绑定需要一组伪元素,但是一旦绑定,您就可以轻松使用过渡和更新/输入/退出周期.但是,渲染与更新/输入/退出和过渡是分离的-由您决定如何以及何时重新绘制可视化效果.该绘图发生在更新/输入/退出和过渡方法之外.

With canvas, data binding requires a set of dummy elements, but, once bound you can easily use transitions and the update/enter/exit cycle. But, rendering is detached from update/enter/exit and transitions - it is up to you to decide how and when to redraw the visualization. This drawing takes place outside of the update/enter/exit and transition methods.

使用svg,可以进入/更新/退出循环并转换可视化中的更新元素,一步即可链接渲染和数据.

With svg, the enter/update/exit cycle and transitions update elements in the visualization, linking rendering and data in one step.

在具有在人造元素上绑定数据的画布中,可视化表示人造节点.在svg中,可视化是节点.

In canvas with data binding on faux elements, the visualization represents the faux nodes. In svg the visualization is the nodes.

数据绑定是一个根本的区别,惯用D3在SVG中需要它,但是可以选择是否在使用Canvas时使用它. 但是,相对于下面提到的D3,Canvas和SVG之间还有其他区别:

Data binding is a fundamental difference, idiomatic D3 requires it in SVG but gives us the choice of whether we want to use it when working with Canvas. However there other differences between Canvas and SVG in relation to D3 mentioned below:

使用Canvas时最令人担心的是它是无状态的,只是像素的集合而不是元素的集合.与特定的渲染形状进行交互时,这会使鼠标事件变得困难.尽管鼠标可以与Canvas交互,但是会触发标准事件以与特定像素进行交互.

Perhaps the most substantial concern with using Canvas is that it is stateless, just a collection of pixels rather than elements. This makes mouse events difficult when interacting with specific rendered shapes. While the mouse can interact with the Canvas, standard events are triggered for interactions with specific pixels.

因此,在使用SVG时,我们可以为强制布局中的每个节点分配一个单击侦听器(例如),而对于Canvas,我们可以在整个画布上设置一个单击侦听器,然后根据位置确定应该考虑哪个节点点击".

So while with SVG we can assign a click listener (for example) to each node in a force layout, with Canvas, we set one click listener fro the entire canvas and then based on position have to determine what node should be considered "clicked".

上面提到的D3力画布示例使用力布局的.find方法并使用查找最接近鼠标单击的节点,然后将拖动主题设置到该节点.

The D3-force canvas example mentioned above uses a force layout's .find method and uses that to find the node closest to a mouse click and then sets the drag subject to that node.

有几种方法可以确定正在与哪些渲染形状进行交互:

There are a few ways we could determine what rendered shape is being interacted with:

  1. 创建一个隐藏的画布,为渲染的形状提供参考图

可见画布中的每个形状都绘制在不可见画布上,但是在不可见画布上具有唯一的颜色.以可见画布上的鼠标事件的xy值为例,我们可以使用它来获得与不可见画布上相同xy的像素颜色.由于颜色是HTML中的数字,因此我们可以将该颜色转换为基准的索引.

  1. 热图/栅格数据的反转比例(将xy位置的比例缩放为未缩放的输入值)(示例)

使用未渲染的Voronoi图的.find方法查找距事件最近的节点(用于点,圆)

Using an unrendered Voronoi diagram's .find method to find nearest node to event (for points, circles)

第一个可能是最常见的,当然也是最灵活的,但其他情况可能会更好,具体取决于上下文.

The first may be the most common, and certainly the most flexible, but the others may be preferable depending on context.

我会很快介绍一下性能.在问题的链接文章" SVG和Canvas有什么区别"中,答案可能不够大胆,但通常,canvas和svg在处理成千上万个节点时的渲染时间有所不同,尤其是在渲染成千上万个正在动画的节点时.

I'll very quickly touch on performance. In the question's linked post "What's the difference between SVG and Canvas" it may not be bold enough in the answers there, but in general canvas and svg differ in rendering time when handling thousands of nodes, especially if rendering thousands of nodes that are being animated.

随着渲染更多的节点以及节点执行更多的操作(转换,移动等),画布的性能变得越来越强.

Canvas becomes increasingly more performant as more nodes are rendered and as the nodes do more things (transition, move, etc).

以下是Canvas(在人造节点上具有数据绑定)和SVG以及19200个同时转换的快速比较:

Here's a quick comparison of Canvas (with data binding on faux nodes) and SVG and 19 200 simultaneous transitions:

  • Canvas Test
  • SVG Test.

画布"应该是两者中较光滑的一个.

The Canvas should be the smoother of the two.

最后,我将介绍D3的模块.其中大多数根本不与DOM交互,并且可以轻松地用于SVG或Canvas.例如,d3-quadtree或d3-time-format不是SVG或Canvas专用的,因为它们根本不处理DOM或渲染.诸如d3-hierarchy之类的模块实际上也不会呈现任何内容,但是会提供在Canvas或SVG中呈现所需的信息.

Lastly I'll touch on D3's modules. Most of these don't interact with the DOM at all and can be used easily for either SVG or Canvas. For example d3-quadtree or d3-time-format aren't SVG or Canvas specific as they don't deal with the DOM or rendering at all. Modules such as d3-hierarchy don't actually render anything either, but provide the information needed to render in either Canvas or SVG.

大多数模块和方法也可以用于生成画布路径方法调用,因此可以相对轻松地用于SVG和Canvas.

Most modules and methods that provide SVG path data can also be used to generate canvas path method calls, and consequently can be used for either SVG and Canvas relatively easily.

我将在这里专门提到几个模块:

I'll mention a couple modules specifically here:

D3选择

显然,此模块需要选择,选择需要元素.因此,要将其与Canvas一起用于输入/更新/退出循环或选择.append/remove/lower/raise之类的内容,我们希望在Canvas中使用人造元素.

Obviously this module requires selections, selections require elements. So to use this with Canvas for things like the enter/update/exit cycle or selection .append/remove/lower/raise we want to use faux elements with Canvas.

使用Canvas,分配有selection.on()的事件侦听器可以在有数据绑定或没有数据绑定的情况下工作,上面提到了鼠标交互的挑战.

With Canvas, event listeners assigned with selection.on() can work with or without data binding, the challenges of mouse interactions are noted above.

D3转换

此模块可转换元素的属性,因此通常仅在我们将数据与人造元素结合使用时,才可以与Canvas一起使用.

This module transitions the properties of elements, so it would generally be used with Canvas only if we were using data binding with faux elements.

D3轴

该模块严格来说是SVG,除非愿意做大量工作以将其拔成Canvas用途.在使用SVG时,尤其是在转换轴时,该模块非常有用.

This module is strictly SVG unless willing to do a fair amount of work to shoehorn it into a Canvas use. This module is extremely useful when working with SVG, especially when transitioning the axis.

D3路径

这将接受Canvas路径命令并将其转换为SVG路径数据.对于将画布代码用于SVG情况很有用.通常在D3内部使用,以生成SVG路径数据.

This takes Canvas path commands and converts them to SVG path data. Useful for taking adopting canvas code to SVG situations. Mostly used internally with D3 to produce SVG path data.

这篇关于dv.js中svg和canvas之间的区别的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆