HTML5 Canvas与SVG与div的比较

539
什么是创建动态元素并能够移动它们的最佳方法?例如,假设我想创建一个矩形、圆形和多边形,然后选择这些对象并移动它们。
我了解到HTML5提供了三个元素可以实现这一点:svgcanvasdiv。对于我想要做的事情,哪一个元素会提供最佳性能?
为了比较这些方法,我想创建三个视觉上相同的网页,每个网页都有页眉、页脚、小部件和文本内容。第一个页面中的小部件将完全使用 canvas 元素创建,第二个使用完全使用 svg 元素,第三个使用纯的 div 元素,HTML和CSS。

1
对于那些刚接触这项技术的人,这个视频涵盖了SVG和Canvas以及它们在HTML5中的集成等其他细节。 - Paulo Bueno
17
简短回答:Canvas 相当于 MS Paint,SVG 相当于 MS Powerpoint。Canvas 是光栅图形,SVG 是矢量图形。 - GetFree
9
亲爱的读者:请对这里的所有比较和陈述持保留态度,并查看帖子和评论的日期。时代在变化,性能和甚至您拥有的选项也会发生改变。例如,大多数答案是在没有WebGL的情况下编写的,但WebGL肯定是一个替代品-它也将在未来几年过时,但是从今天开始它可能非常相关。 - Sebastian
@Sebastian,今天你会推荐什么?如果给定一个基本大小(例如1280x800),并且你愿意在代码中手动缩放元素或始终使用百分比,那么使用SVG相对于DIV有什么优势吗? - Crashalot
1
@Crashalot - 没有单一的最佳解决方案。请参见我的答案以及这里获取更多评论。如果您只需要几个矩形或圆角矩形,则使用div即可。我更喜欢SVG,因为它简单明了,坐标明确(免受CSS更改的影响),而且比div更好用。当然,使用HTML可以更轻松地进行复杂文本渲染。如果只是矩形(但有成千上万个),则WebGL值得一看,但否则过度设计。 - Sebastian
显示剩余5条评论
10个回答

644

简短回答:

对于你来说,SVG会更加容易,因为它已经内置了选择和移动对象功能。SVG对象是DOM对象,因此它们具有“click”处理程序等功能。

DIV可以使用,但是当加载大量时性能较差且不太灵活。

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


详细解释:

HTML5 Canvas仅是一个位图绘图表面。您设置要绘制的东西(例如颜色和线条粗细),然后将其绘制出来,然后Canvas不知道绘制的内容在哪里或是什么,它仅仅是像素。如果您想绘制矩形并使其移动或可选择,则必须从头开始编写所有代码,包括记住您绘制的内容的代码。

另一方面,SVG必须保持对其呈现的每个对象的引用。您创建的每个SVG / VML元素都是DOM中的一个真实元素。默认情况下,这允许您更好地跟踪创建的元素,并通过默认方式更轻松地处理诸如鼠标事件之类的事情,但是当存在大量对象时,速度会显着降低。

那些SVG DOM引用意味着处理您绘制的内容的某些工作已经为您完成了。当渲染非常大的对象时,SVG更快,但是在渲染许多对象时较慢。

对于游戏,Canvas可能会更快。对于大型地图程序,则使用SVG可能会更快。如果您确实想使用Canvas,则我有一些关于如何启用可移动对象的教程在这里

如果要进行更快的事情和重度位图操作(例如动画),则Canvas更好,但如果要进行大量互动,则需要编写更多代码。

我对基于HTML DIV和Canvas的绘图做了一些比较测试。虽然我可以写一篇关于二者优缺点的巨型文章,但是以下是我测试的一些相关结果,供您考虑特定应用:

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

我在这两个测试页面中均添加了10万个节点。它们的性能表现截然不同:

HTML测试标签页需要很长时间才能加载(计时略小于5分钟,Chrome第一次打开时询问是否关闭该页面)。Chrome任务管理器显示该标签页占用了168MB。当我观看该页面时,CPU时间占用率为12-13%,当我没有观看时,CPU时间占用率为0%。

Canvas标签页1秒钟即可加载,并且只占用30MB。它也一直占用13%的CPU时间,无论是否正在查看它。(2013年的编辑:他们大多已经修复了这个问题)

在HTML页面上拖动更加平滑,这是设计所预期的,因为当前设置是每30毫秒重新绘制Canvas测试中的所有内容。Canvas还有很多优化空间(例如canvas失效、欧拉剪辑、选择性重绘等等,这取决于您想要实现多少)。

毫无疑问,您可以让Canvas在对象操作方面比简单测试中的div更快,并且在加载时间上当然也更快。Canvas的绘制/加载速度更快,而且还有更多优化空间(例如,排除屏幕外的元素非常容易)。

结论:

  • 对于少量项目(少于1000?实际情况可能因应用而异),SVG可能更好
  • 对于数千个对象和精细操作,Canvas更好,但需要更多代码(或库)来启动它
  • HTML Divs不灵活,不易扩展。只能通过设置圆角实现绘制圆形,创建复杂形状则需要成百上千个细小像素宽度的Divs。这很疯狂。

4
Cake 库是在画布上实现移动对象和动画的另一个例子。 - SiggyF
如果浏览器使用了硬件加速的CSS引擎,那么div可以进行缩放。但是CSS艺术与Canvas和SVG不同,这里应该选择Canvas和SVG。CSS艺术/ div艺术只有在您不需要过度渲染时才会使用,比如小型叠加层。 - ShrekOverflow
关于DIV,如果您想制作圆形/特殊形状,并且不会在过程中更改其图像/精灵,则可以创建PNG并将其用作“background-image”... 尽管您也可以在SVG / Canvas中做类似的事情 - luiges90
4
如果你正在创建一个互动式地图游戏,该怎么办?:p - Anthony
我正在比较数十万个移动对象,而不是一百个左右。 - Simon Sarris
显示剩余3条评论

41

此外,我一直在做一个图表应用程序,并最初使用了canvas。该图表包含许多节点,它们可能相当大。用户可以在图表中拖动元素。

我发现,在我的Mac上,对于非常大的图像,SVG更加优越。我有一台2013年的MacBook Pro 13英寸Retina,当用户在图表中拖动对象时,它可以很好地运行下面的示例。该图像为6000x6000像素,并且有1000个对象。在canvas中进行类似的构建对于我来说是不可能进行动画的。

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

示例:http://jsfiddle.net/knutsi/PUcr8/16/

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

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);

2
我们在尝试了很多方法都无法让Canvas正常工作后,最终选择了SVG。我们有一个非常大的图表,而SVG是迄今为止最有效的选择,此外,在视网膜屏幕上的自动缩放功能也是一个巨大的优势。 - Fijjit
Knut和@Fijjit,你们考虑过使用DIV代替SVG吗?如果给定一个基本大小(例如1280x800),你们能否手动缩放DIV,使它们看起来像SVG一样清晰?感谢你们的帮助! - Crashalot

28

13
为什么有人说Canvas依赖分辨率?我知道一旦位图被渲染,它就不能很好地缩放。但是你可以在分辨率大小更改时重新绘制,那么为什么它不是分辨率无关的呢? 有人说Canvas依赖分辨率是因为渲染后的位图不能很好地缩放。尽管如此,通过重新绘制来适应分辨率大小的变化,可以实现分辨率无关。 - Alex Bollbach
6
@AlexBollbach说:Canvas的分辨率依赖性很强,因为你需要考虑(依赖)分辨率才能获得良好的结果。但是SVG与分辨率无关,所以不用担心在2400DPI打印机和基于Canvas的渲染中出现锯齿状线条。而SVG则没有这个问题。 - Sebastian

22

虽然以上回答大多数仍然有一定的真实性,但我认为它们需要更新:

多年来,SVG的性能有了很大的提升,现在有支持硬件加速的CSS转换和动画功能,不需要依赖于JavaScript性能。当然,JavaScript的性能也有所改善,Canvas的性能也随之提高,但是SVG得到的改进远远超过了这些。此外,现在几乎所有浏览器都支持一个“新生代”技术WebGL。用Simon上面的话来说:WebGL毫无疑问胜过Canvas和SVG。不过这并不意味着它应该成为首选技术,因为使用它需要付出更多的努力,而且它只在非常特定的用例中才更快。

我认为,在大多数用例中,SVG给出了最佳的性能/易用性比。视觉化必须非常复杂(就元素数量而言),同时每个元素非常简单,这样Canvas甚至WebGL才能真正发挥优势。

类似问题的回答中,我提供了更多细节,说明为什么三种技术相结合有时是您最好的选择。


1
Unix用户应该注意,Firefox和Chromium在默认情况下禁用了硬件加速,在2019年中期仍然是如此。 - NVRM
1
总结当前情况:在Linux上,我在Chrome和Chromium上测试通过。在2019年的所有实例中,我都没有进行特殊配置。Firefox/Mozilla正在为Linux开发此功能,但是对于SVG、CSS等,基于进程的渲染对于FF并不新鲜,而且总是比Canvas更好。 - Sebastian
@Sebastian,你是唯一提到“现在有硬件加速的CSS转换和动画支持SVG”的答案。这是否意味着只有CSS编写的过渡/动画才会受到硬件加速?我一直在研究用JavaScript API实现SVG动画,但基于这个答案,我现在在想是否应该转而采用CSS方法(以获得更好的性能)。 - ecoe
@ecoe - 这取决于你正在寻找哪些其他方法。一旦涉及到JS(每帧),肯定可以在没有JS的情况下更快。如果JS仅用于设置动画,那么可能还好。如果JS运行并计算每个帧,就像Canvas和大多数WebGL一样,它永远无法像这样有效。 - Sebastian
1
@Sebastian 谢谢,我阅读了你在另一个答案中提到的博客,但它只展示了在一个“复杂”的测试用例中SVG比Canvas更快,并且他们指出: where only simple... ...the performance of Canvas is actually better if you have lots and lots of elements on the screen at the same time.这是我见过的最好的研究,交互式演示非常棒!尽管通过我的自己的测试,我没有能够看到svg/canvas在复杂情况下有多大差异,所以我仍然想知道SVG是否能够胜过Canvas? - ecoe
显示剩余4条评论

19

我同意Simon Sarris的结论:

我比较了一些Protovis(SVG)和Processingjs(Canvas)中显示超过2000个点的可视化效果,processingjs比protovis快得多。

使用SVG处理事件当然更容易,因为你可以将它们附加到对象上。在Canvas中,您必须手动执行此操作(检查鼠标位置等),但对于简单的交互,这应该不难。

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

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


18

关于使用div选项,这是我的两分钱。

Famous/Infamous和SamsaraJS(可能还有其他库)使用非嵌套的绝对定位的div(包含较复杂的HTML/CSS内容),结合matrix2d/matrix3d进行定位和2D/3D变换,在中等移动设备上实现了稳定的60FPS,因此我认为div并不是一个慢选项。

在YouTube和其他地方有许多屏幕录像,高性能的2D/3D内容在浏览器中运行,其中所有元素都是DOM元素,您可以在其上“检查元素”,实现了60FPS的流畅演示(与WebGL混合使用以实现某些效果,但并非主要渲染部分)。


13

针对您的需求,我建议使用SVG。因为它包含DOM事件(如鼠标处理、拖放等),无需实现自己的重绘,并且无需跟踪对象状态。当您需要进行位图图像操作时,请使用Canvas。当您想要操作在HTML中创建的内容时,请使用普通的div。关于性能,您会发现现代浏览器都在加速这三种技术,但是Canvas目前得到了最多的关注。另一方面,良好编写的JavaScript对于使用Canvas获取最佳性能至关重要,因此我仍然建议使用SVG。


1
实际上,使用纯HTML与CSS图像相结合是最高效的。 - Raynos

5

所有技术都有优点和缺点,下面我们来比较一下。

Canvas在总体性能方面表现最佳,但前提是使用正确。

Divs:

    • 良好的性能表现
    • 可以使用DOM进行操作
    • 可以访问DOM事件
    • CSS支持
    • 难以制作复杂形状

性能测试: https://kajam.hg0428.repl.co/pref/

Canvas:

    • 更好的形状支持
    • 出色的性能表现
    • 出色的浏览器支持
    • 无CSS支持

性能测试: https://js-game-engine.hg0428.repl.co/canvasTest/preform.html

SVGs:

    • 更好的形状支持
    • 使用起来更困难
    • 良好的浏览器支持
    • 没有CSS支持,但有许多不同的SVG元素
    • 性能较差

我还没有为它做过性能测试,但基于其他测试结果,它的表现不好。

要使Canvas快速:

Canvas可以具有非常动态的性能,因此让我们回顾一些技巧。 避免使用 ctx.rectctx.fill,改用 ctx.fillRect,这是最重要的一点,它甚至可以破坏最简单的游戏。 使用形状的 fillstroke,而是使用 fill[Shape]

如果你忘记了在Canvas中使用这些技巧,你的游戏会很慢。我通过经验得出了这个结论。


1
画布性能测试:preform.js:36 未捕获的类型错误:无法设置未定义的属性(设置“type”) 在OnDocumentClick(preform.js:36:15) - Jason S

4

在谷歌搜索时,我发现了一篇关于使用和压缩SVGCanvas的好解释,链接为http://teropa.info/blog/2016/12/12/graphics-in-angular-2.html

希望对你有所帮助:

  • SVG和HTML一样,使用保留渲染:当我们想在屏幕上绘制一个矩形时,在DOM中我们声明使用一个<rect>元素。浏览器会绘制一个矩形,但同时也会创建一个代表该矩形的内存中的SVGRectElement对象。这个对象将一直存在,供我们操作-它是被保留的。我们可以随时间分配不同的位置和大小给它。我们还可以附加事件监听器使其交互。
  • Canvas使用即时渲染:当我们绘制一个矩形时,浏览器立即在屏幕上呈现一个矩形,但永远不会有任何代表它的“矩形对象”。只有画布缓冲区中的一堆像素。我们无法移动矩形。我们只能绘制另一个矩形。我们无法响应单击或其他事件,只能响应整个画布上的事件。

因此,Canvas比SVG更低级别、限制性的API。但是,这也有一个反面,就是使用相同数量的资源,你可以做更多的事情。因为浏览器不必创建和维护我们绘制的所有东西的内存中对象图,所以在绘制相同的视觉场景时,需要更少的内存和计算资源。如果你有一个非常大而复杂的可视化要绘制,Canvas可能是你的选择。


0

该工具输出HTML/CSS常规多边形,而不是canvas或SVG(声明,我编写了它)https://html-polygon.com

这可能更容易用于交互性,因为结果是真正存在于HTML DOM中的HTML元素。

目前只有React和Vue包,但网站上的试玩工具可以在您不使用这些包且不需要动态生成的多边形形状时暴露出生成的HTML。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接