521

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

我知道 HTML5 提供了三个可以实现这一点的元素:svgcanvasdiv。对于我想做的事情,其中​​哪一个元素将提供最佳性能?

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

4

9 回答 9

618

简短的回答:

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

DIV 还可以,但很笨重,并且大量加载时性能很差。

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


长答案:

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

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

那些 SVG DOM 引用意味着处理您绘制的事物的一些步骤是为您完成的。SVG 在渲染非常大的对象时速度更快,但在渲染许多对象时速度较慢。

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

画布对于更快的事情和繁重的位图操作(如动画)会更好,但如果你想要大量的交互性,它将需要更多的代码。

我在 HTML DIV 制作的绘图与 Canvas 制作的绘图上运行了一堆数字。我可以就每种方法的好处发表一篇大文章,但我会给出一些我的测试的相关结果,以供您针对您的特定应用考虑:

我制作了 Canvas 和 HTML DIV 测试页面,它们都有可移动的“节点”。画布节点是我在 Javascript 中创建并跟踪的对象。HTML 节点是可移动的 Div。

我为我的两个测试中的每一个添加了 100,000 个节点。他们的表现截然不同:

HTML 测试选项卡需要很长时间才能加载(时间略低于 5 分钟,chrome 第一次要求终止该页面)。Chrome 的任务管理器说该选项卡占用了 168MB。当我看它时,它占用了 12-13% 的 CPU 时间,当我不看它时,它占用了 0%。

Canvas 选项卡在一秒钟内加载,占用 30MB。它也一直占用 13% 的 CPU 时间,无论是否有人在看它。(2013 年编辑:他们基本上已经解决了这个问题)

在 HTML 页面上拖动更顺畅,这是设计所期望的,因为当前设置是在 Canvas 测试中每 30 毫秒重绘所有内容。为此,Canvas 有很多优化。(画布失效是最简单的,还有裁剪区域、选择性重绘等。这取决于你想实现的程度)

毫无疑问,在这个简单的测试中,您可以让 Canvas 在对象操作方面像 div 一样更快,当然在加载时间上也快得多。Canvas 中的绘图/加载速度更快,并且还有更多的优化空间(即,排除屏幕外的内容非常容易)。

结论:

  • SVG 可能更适合项目很少的应用程序和应用程序(少于 1000 个?真的取决于)
  • Canvas 更适合成千上万的对象和仔细的操作,但需要更多的代码(或库)才能让它落地。
  • HTML div 很笨重且无法缩放,只能用圆角制作圆形,制作复杂的形状是可能的,但涉及数百个微小的像素宽 div。疯狂随之而来。
于 2011-05-04T12:02:21.693 回答
41

除此之外,我一直在做一个图表应用程序,最初是从画布开始的。该图由许多节点组成,它们可以变得很大。用户可以拖动图表中的元素。

我发现在我的 Mac 上,对于非常大的图像,SVG 更胜一筹。我有一个 MacBook Pro 2013 13" Retina,它在下面运行得很好。图像是 6000x6000 像素,有 1000 个对象。当用户在图表。

在现代显示器上,您还必须考虑不同的分辨率,而 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);
于 2014-01-26T04:25:26.113 回答
29

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

帆布

SVG

  • 独立于分辨率
  • 支持事件处理程序
  • 最适合具有大渲染区域的应用程序(谷歌地图)
  • 如果复杂则渲染缓慢(任何使用 DOM 的东西都会很慢)
  • 不适合游戏应用
于 2016-02-28T14:14:06.970 回答
20

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

多年来,SVG 的性能有了很大的提高,现在有硬件加速的 CSS 过渡和动画,它们完全不依赖于 JavaScript 性能。当然,JavaScript 的性能也得到了提升,Canvas 的性能也随之提升,但没有 SVG 的提升那么多。现在几乎所有浏览器都可以使用该块上的“新孩子”,那就是WebGL。使用 Simon 上面使用的相同的话:它击败了 Canvas 和 SVG。不过,这并不意味着它应该是首选技术,因为它是一种可以使用的野兽,而且它只会在非常特定的用例中更快。

恕我直言,对于当今的大多数用例,SVG 提供了最佳的性能/可用性比。可视化需要非常复杂(相对于元素的数量)并且同时非常简单(每个元素),这样 Canvas 甚至 WebGL 才能真正发光。

对类似问题的回答中,我提供了更多详细信息,为什么我认为有时将所有三种技术结合起来是您拥有的最佳选择。

于 2018-04-07T17:20:58.890 回答
19

我同意 Simon Sarris 的结论:

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

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

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

以下是一些有趣的基准:

于 2011-05-05T09:39:26.673 回答
18

只是我关于 divs 选项的 2 美分。

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

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

于 2015-12-05T01:45:05.743 回答
13

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

于 2011-05-04T11:55:35.323 回答
4

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

希望能帮助到你:

  • SVG 和 HTML 一样,使用保留渲染:当我们想在屏幕上绘制一个矩形时,我们在 DOM 中声明式地使用一个元素。然后浏览器将绘制一个矩形,但它也会在内存中创建一个表示该矩形的SVGRectElement对象。这个对象是我们可以操纵的东西——它被保留了。随着时间的推移,我们可以为其分配不同的位置和大小。我们还可以附加事件侦听器以使其具有交互性。
  • Canvas 使用立即渲染:当我们绘制一个矩形时,浏览器会立即在屏幕上渲染一个矩形,但永远不会有任何“矩形对象”来表示它。画布缓冲区中只有一堆像素。我们不能移动矩形。我们只能再画一个矩形。我们无法响应矩形上的点击或其他事件。我们只能响应整个画布上的事件。

所以 canvas 是一个比 SVG 更底层、更严格的 API。但有一个反面,那就是使用画布,你可以用相同数量的资源做更多的事情。因为浏览器不必创建和维护我们绘制的所有事物的内存对象图,它需要更少的内存和计算资源来绘制相同的视觉场景。如果您要绘制非常大且复杂的可视化效果,Canvas 可能是您的选择。

于 2018-03-03T07:37:46.277 回答
1

他们都有好事和坏事,所以让我们在下面进行比较。

Canvas 将具有整体最佳性能,但前提是您正确使用它。

分区:

    • 很好的表现
    • 您可以使用 DOM 操作它
    • 您有权访问 DOM 事件
    • CSS 支持
  1. 很难做出复杂的形状

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

帆布:

    • 更好的形状支持
    • 很棒的演出
    • 强大的浏览器支持
  1. 没有 CSS

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

SVG:

    • 更好的形状支持
  1. 对我们来说更难
    • 良好的浏览器支持
  2. 没有 CSS,但是有很多不同的 SVG 东西
  3. 糟糕的表现

我还没有对此进行性能测试,但基于其他测试,它非常糟糕。

**

使 Canvas 快速:

** Canvas 可以有非常动态的性能,所以让我们回顾一些技巧。避免使用ctx.rectand ctx.fillctx.fillRect改用,这是最大的一个,它甚至可以毁掉最简单的游戏。不要将形状与它们的filland一起stroke使用,fill[Shape]而是使用。

如果您不记得使用画布时,您的游戏将被毁掉。我从经验中学到了这一点。

于 2021-12-01T23:07:26.767 回答