关于javascript:HTML5 Canvas vs. SVG vs. div

HTML5 Canvas vs. SVG vs. div

什么是动态创建元素并能够移动它们的最佳方法? 例如,假设我想创建一个矩形,圆形和多边形,然后选择这些对象并移动它们。

我知道HTML5提供了三个可以实现这一目标的元素:svg,canvas和div。 对于我想要做的事情,哪一个元素将提供最佳性能?

为了比较这些方法,我考虑创建三个视觉上相同的网页,每个网页都有页眉,页脚,小部件和文本内容。 第一页中的窗口小部件将完全使用canvas元素创建,第二部分完全使用svg元素创建,第三部分使用纯div元素,HTML和CSS创建。


简短的回答:

SVG对你来说会更容易,因为已经内置了选择和移动它.SVG对象是DOM对象,所以它们有"点击"处理程序等。

DIV很好,但很笨重,而且大量的性能负载很差。

Canvas具有最佳性能,但您必须自己实现托管状态(对象选择等)的所有概念,或使用库。

答案很长:

HTML5 Canvas只是位图的绘图表面。你设置绘制(用颜色和线条粗细说),绘制那个东西,然后画布不知道那个东西:它不知道它在哪里或者你刚刚绘制的是什么,它是只是像素。如果你想绘制矩形并让它们四处移动或可选择,那么你必须从头开始编写所有这些代码,包括记住你绘制它们的代码。

另一方面,SVG必须维护对它呈现的每个对象的引用。您创建的每个SVG / VML元素都是DOM中的真实元素。默认情况下,这可以让您更好地跟踪您创建的元素,并且默认情况下更容易处理鼠标事件,但是当存在大量对象时,它会显着减慢速度

那些SVG DOM参考意味着处理你绘制的东西的一些步法是为你完成的。渲染非常大的对象时SVG速度更快,渲染许多对象时速度更慢。

Canvas中的游戏可能会更快。在SVG中,一个巨大的地图程序可能会更快。如果你想使用Canvas,我有一些关于在这里启动和运行可移动对象的教程。

Canvas对于更快的事物和繁重的位图操作(如动画)会更好,但如果你想要很多交互性,它会占用更多的代码。

我在HTML DIV制作的绘图和Canvas制作的绘图上运行了一堆数字。我可以发表一篇关于每个问题的好处的帖子,但是我会根据您的具体应用考虑我的测试的一些相关结果:

我制作了Canvas和HTML DIV测试页面,两者都有可移动的"节点"。 Canvas节点是我创建的对象,并在Javascript中跟踪。 HTML节点是可移动的Div。

我在两次测试中都添加了100,000个节点。他们表现完全不同:

HTML测试选项卡需要永久加载(时间略短于5分钟,Chrome要求首次杀死页面)。 Chrome的任务经理表示该标签占用了168MB。当我看着它时占用12-13%的CPU时间,当我不看时它占用0%。

"画布"选项卡在一秒钟内加载,占用30MB。它总是占用CPU时间的13%,无论是否正在查看它。 (2013年编辑:他们大多修复了)

在HTML页面上拖动更加平滑,这是设计所期望的,因为当前设置是在Canvas测试中每30毫秒重绘一次。 Canvas有很多优化可供选择。 (画布失效是最简单的,也是裁剪区域,选择性重绘等等。只取决于你实现的感觉)

毫无疑问,你可以让Canvas在对象操作中更快地成为那个简单测试中的div,当然在加载时间里要快得多。 Canvas中的绘图/加载速度更快,并且具有更多的优化空间(即,排除屏幕外的内容非常容易)。

结论:

  • SVG可能更适用于少量项目的应用程序和应用程序(少于1000个?真的取决于)
  • 对于成千上万的对象和仔细操作,Canvas更好,但需要更多的代码(或库)来实现它。
  • HTML Divs很笨重,不能缩放,只能使用圆角来制作圆形,这使得复杂的形状成为可能,但涉及数百个微小的像素范围的div。疯狂随之而来。


为了补充这一点,我一直在做一个图表应用程序,最初开始使用canvas。该图由许多节点组成,它们可以变得非常大。用户可以拖动图中的元素。

我发现在我的Mac上,对于非常大的图像,SVG是优越的。我有一台MacBook Pro 2013 13"Retina,它在下面运行得很好。图像是6000x6000像素,有1000个物体。当用户拖动物体时,画布中的类似结构不可能为我设置动画。图。

在现代显示器上,您还必须考虑不同的分辨率,这里SVG免费为您提供所有这些。

小提琴:http://jsfiddle.net/knutsi/PUcr8/16/

全屏:http://jsfiddle.net/knutsi/PUcr8/16/embedded/result/

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
var wiggle_factor = 0.0;
nodes = [];

// create svg:
var svg = document.createElementNS("http://www.w3.org/2000/svg","svg");
svg.setAttribute('style', 'border: 1px solid black');
svg.setAttribute('width', '6000');
svg.setAttribute('height', '6000');

svg.setAttributeNS("http://www.w3.org/2000/xmlns/","xmlns:xlink",
   "http://www.w3.org/1999/xlink");

document.body.appendChild(svg);


function makeNode(wiggle) {
    var node = document.createElementNS("http://www.w3.org/2000/svg","g");
    var node_x = (Math.random() * 6000);
    var node_y = (Math.random() * 6000);
    node.setAttribute("transform","translate(" + node_x +"," + node_y +")");

    // circle:
    var circ = document.createElementNS("http://www.w3.org/2000/svg","circle");
    circ.setAttribute("id","cir")
    circ.setAttribute("cx", 0 +"px")
    circ.setAttribute("cy", 0 +"px")
    circ.setAttribute("r","100px");
    circ.setAttribute('fill', 'red');
    circ.setAttribute('pointer-events', 'inherit')

    // text:
    var text = document.createElementNS("http://www.w3.org/2000/svg","text");
    text.textContent ="This is a test! ???";

    node.appendChild(circ);
    node.appendChild(text);

    node.x = node_x;
    node.y = node_y;

    if(wiggle)
        nodes.push(node)
    return node;
}

// populate with 1000 nodes:
for(var i = 0; i < 1000; i++) {
    var node = makeNode(true);
    svg.appendChild(node);
}

// make one mapped to mouse:
var bnode = makeNode(false);
svg.appendChild(bnode);

document.body.onmousemove=function(event){
    bnode.setAttribute("transform","translate(" +
        (event.clientX + window.pageXOffset) +"," +
        (event.clientY + window.pageYOffset) +")");
};

setInterval(function() {
    wiggle_factor += 1/60;
    nodes.forEach(function(node) {

        node.setAttribute("transform","translate("
                          + (Math.sin(wiggle_factor) * 200 + node.x)
                          +","
                          + (Math.sin(wiggle_factor) * 200 + node.y)
                          +")");        
    })
},1000/60);


了解SVG和Canvas之间的差异将有助于选择正确的。

帆布

  • 取决于分辨率
  • 不支持事件处理程序
  • 文本呈现能力差
  • 您可以将生成的图像另存为.png或.jpg
  • 非常适合图形密集型游戏

SVG

  • 决议独立
  • 支持事件处理程序
  • 最适合具有大渲染区域的应用程序(谷歌地图)
  • 缓慢渲染,如果复杂(任何使用DOM很多的东西)
    慢)
  • 不适合游戏应用


我同意Simon Sarris的结论:

我将Protovis(SVG)中的一些可视化与Processingjs(Canvas)进行了比较,后者显示> 2000点,处理js比protovis快得多。

使用SVG处理事件当然要容易得多,因为您可以将它们附加到对象上。在Canvas中你必须手动完成(检查鼠标位置等),但是为了简单的交互,它应该不难。

还有dojo工具包的dojo.gfx库。它提供了一个抽象层,您可以指定渲染器(SVG,Canvas,Silverlight)。这可能也是一个可行的选择,虽然我不知道额外的抽象层增加多少开销,但它使编码交互和动画变得容易,并且与渲染器无关。

以下是一些有趣的基准测试:

  • http://svbreakaway.info/tp.php#jan21a
  • http://www.eleqtriq.com/2010/02/canvas-svg-flash/
  • http://smus.com/canvas-vs-svg-performance/

关于div选项只需2美分。

Famous / Infamous和SamsaraJS(可能还有其他人)使用绝对定位的非嵌套div(具有非平凡的HTML / CSS内容),结合matrix2d / matrix3d进行定位和2D / 3D转换,并在中等移动硬件上实现稳定的60FPS所以我认为反对div是一个缓慢的选择。

在Youtube和其他地方有大量的屏幕录制,在浏览器中运行的高性能2D / 3D内容,所有内容都是一个DOM元素,你可以在60FPS上检查元素(与WebGL混合用于某些效果,但不适用于渲染的主要部分)。


虽然上面的大多数答案仍然有一些道理,但我认为它们值得更新:

多年来,SVG的性能已经有了很大提高,现在有了硬件加速的CSS转换和SVG动画,完全不依赖于JavaScript性能。当然,JavaScript性能也得到了提升,Canvas的性能也得到了提升,但没有SVG得到改进的那么多。此外,该块上还有一个"新手",几乎所有浏览器都可以使用它,也就是WebGL。使用Simon上面使用的相同单词:它击败了Canvas和SVG。然而,这并不意味着它应该是首选技术,因为它是一个可以使用的野兽,它在非常具体的用例中更快。

对于大多数用例来说,恕我直言,SVG提供了最佳的性能/可用性比率。可视化需要非常复杂(关于元素的数量)并且同时非常简单(每个元素),因此Canvas甚至更加如此WebGL真正闪耀。

在这个类似问题的答案中,我提供了更多详细信息,为什么我认为所有这三种技术的组合有时是您的最佳选择。


出于您的目的,我建议使用SVG,因为您获得了DOM事件,如鼠标处理,包括拖放,包括,您不必实现自己的重绘,并且您不必跟踪状态 你的对象。 当您需要进行位图图像处理时使用Canvas,并在想要操作HTML中创建的内容时使用常规div。 至于性能,你会发现现代浏览器正在加速这三种,但到目前为止,该画布受到了最多的关注。 另一方面,你用javascript编写javascript对于获得最佳性能至关重要,所以我仍然建议使用SVG。


谷歌搜索时,我在http://teropa.info/blog/2016/12/12/graphics-in-angular-2.html找到了关于SVG和Canvas的使用和压缩的一个很好的解释。

希望能帮助到你:

  • SVG, like HTML, uses retained rendering: When we want to draw a
    rectangle on the screen, we declaratively use a element in our
    DOM. The browser will then draw a rectangle, but it will also create
    an in-memory SVGRectElement object that represents the rectangle. This
    object is something that sticks around for us to manipulate – it is
    retained. We can assign different positions and sizes to it over time.
    We can also attach event listeners to make it interactive.
  • Canvas uses immediate rendering: When we draw a rectangle, the browser immediately renders a rectangle on the screen, but there is
    never going to be any"rectangle object" that represents it. There's
    just a bunch of pixels in the canvas buffer. We can't move the
    rectangle. We can only draw another rectangle. We can't respond to
    clicks or other events on the rectangle. We can only respond to events
    on the whole canvas.

So canvas is a more low-level, restrictive API than SVG. But there's a
flipside to that, which is that with canvas you can do more with the
same amount of resources. Because the browser does not have to create
and maintain the in-memory object graph of all the things we have
drawn, it needs less memory and computation resources to draw the same
visual scene. If you have a very large and complex visualization to
draw, Canvas may be your ticket.