HTML5 Canvas、SVG和div

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

我知道HTML5提供了三个元素来实现这一点:svg帆布div。对于我想做的事情,这些元素中哪一个会提供最好的性能?

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

156103 次浏览

出于您的目的,我建议使用SVG,因为您获得了DOM事件,如鼠标处理(包括拖放),因此您不必实现自己的重绘制,也不必跟踪对象的状态。当你必须做位图图像操作时使用Canvas,当你想操作在HTML中创建的东西时使用常规div。至于性能,你会发现现在的浏览器都在加速这三个方面,但迄今为止画布受到了最多的关注。另一方面,javascript编写得好不好是获得canvas最佳性能的关键,因此我仍然建议使用SVG。

简单的回答是:

SVG应该是更容易,因为已经内置了选择和移动它。SVG对象是DOM对象,因此它们有“点击”处理程序等。

div是可以的,但是很笨重,并且在大量加载时具有可怕的性能。

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


长话短说:

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

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

这些SVG DOM引用意味着一些处理所绘制事物的步法已经为您完成了。SVG在呈现非常大的对象时更快,但在呈现许多对象时更慢。

在Canvas中游戏可能会更快。大型地图程序使用SVG可能会更快。如果你确实想使用画布,我有一些教程获得可移动的对象,并运行在这里

Canvas更适合于快速的内容和重位图操作(如动画),但如果你想要大量的交互性,则需要更多的代码。

我在HTML div制作的绘图和canvas制作的绘图上运行了一堆数字。我可以写一篇关于每种方法的好处的文章,但我将给出一些相关的测试结果,以供您考虑特定的应用程序:

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

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

HTML测试标签永远加载(时间略低于5分钟,chrome要求第一次杀死页面)。Chrome的任务管理器说这个选项卡占用了168MB。当我看着它时,它会占用12-13%的CPU时间,当我不看着它时,它会占用0%的CPU时间。

Canvas标签在一秒内加载,占用30MB。它还会一直占用13%的CPU时间,无论你是否在看它。# EYZ0

在HTML页面上拖动更流畅,这是设计所期望的,因为当前的设置是在Canvas测试中每30毫秒重绘EVERYTHING。为此,Canvas有很多优化。(画布失效是最简单的,还有剪切区域,选择性重绘等。这取决于你有多喜欢执行)

毫无疑问,在这个简单的测试中,你可以让Canvas在对象操作上更快,当然在加载时间上也更快。在Canvas中绘制/加载更快,也有更多的优化空间(也就是说,排除屏幕外的东西是非常容易的)。

结论:

  • SVG可能更适合应用程序和项目较少的应用程序(少于1000个?取决于真的)
  • Canvas更适合成千上万的对象和仔细的操作,但需要更多的代码(或库)才能使它起步。
  • HTML div是笨拙的,不能缩放,制作一个圆只能用圆角,制作复杂的形状是可能的,但涉及到数百个极小的像素宽的div。疯狂。

我同意Simon Sarris的结论:

我比较了Protovis (SVG)和Processingjs (Canvas)中的一些可视化,后者显示> 2000点,Processingjs比Protovis快得多。

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

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

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

为了补充这一点,我一直在做一个图表应用程序,最初是从画布开始的。该图由许多节点组成,而且它们可以变得相当大。用户可以在图中拖动元素。

我发现,在我的Mac上,对于非常大的图像,SVG更好。我有一台MacBook Pro 2013 13英寸Retina,它在下面的操作非常好。图像是6000x6000像素,有1000个对象。当用户在图中拖动对象时,canvas中的类似结构对我来说是不可能进行动画化的。

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

小提琴:# EYZ0

全屏:# EYZ0

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

只是我对divs选项的意见。

《Famous/Infamous》和《samyazare》(可能还有其他游戏)使用绝对定位的非嵌套divs(带有非繁琐的HTML/CSS内容),结合matrix2d/matrix3d进行定位和2D/3D转换,并在中等移动硬件上实现了稳定的60FPS,所以我不认为divs是一个缓慢的选择。

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

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

帆布

SVG

  • 分辨率独立
  • 支持事件处理程序
  • 最适合具有大渲染区域的应用程序(谷歌Maps)
  • 如果复杂,渲染缓慢(任何大量使用DOM的东西都会如此) 李慢)< / >
  • 不适合游戏应用

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

希望能有所帮助:

    SVG,像HTML一样,使用保留的呈现:当我们想要绘制一个 矩形中,我们声明式地使用元素 DOM。然后浏览器将绘制一个矩形,但它也将创建 内存中表示矩形的SVGRectElement对象。这 对象是停留在我们周围让我们操纵的东西-是的 保留。随着时间的推移,我们可以给它分配不同的位置和大小。 我们还可以附加事件监听器,使其具有交互性
  • Canvas使用立即呈现:当我们使用画一个矩形时,浏览器立即在屏幕上呈现一个矩形,但确实存在 永远不会有任何“矩形对象”来表示它。有 只是画布缓冲区中的一堆像素。我们动不了 矩形。我们只能再画一个矩形。我们无法回应 矩形上的单击或其他事件。我们只能对事件作出反应 在整个画布。李< / >
所以canvas是一个比SVG更低级、更有限制性的API。但是有一个 另一方面,就是你可以用canvas做更多 同样数量的资源。因为浏览器不需要创建 维护内存中所有东西的对象图 绘制时,需要较少的内存和计算资源 视觉场景。如果你有一个非常大和复杂的可视化

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

多年来,SVG的性能有了很大的提高,现在有了硬件加速CSS转换和SVG动画,它完全不依赖于JavaScript的性能。当然,JavaScript的性能也得到了改善,Canvas的性能也得到了改善,但改善的程度不及SVG。此外,现在几乎所有浏览器都可以使用WebGL。用Simon上面用过的同样的词:It 胜过Canvas和SVG hands down。不过,这并不意味着它应该成为首选技术,因为它很难使用,而且只在非常特定的用例中速度更快。

恕我直言,对于今天的大多数用例,SVG提供了最好的性能/可用性比率。可视化需要非常复杂(就元素数量而言),同时又非常简单(每个元素),这样Canvas和WebGL才能真正发挥作用。

这是一个类似问题的答案中,我提供了更多的细节,为什么我认为结合是所有三种技术中有时是最好的选择。

它们都有好的方面和坏的方面,下面我们来比较一下。

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

< >强div: < / >强

    • 良好的性能
    • 您可以使用DOM操作它
    • 您可以访问DOM事件
    • CSS支持
    • 很难做出复杂的形状

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

< >强画布:< / >强

    • 更好的形状支撑
    • 伟大的成就
    • 强大的浏览器支持
    • 没有CSS

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

< >强svg: < / >强

    • 更好的形状支撑
    • 更难使用
    • 良好的浏览器支持
    • 没有CSS,但有很多不同的SVG
    • 糟糕的性能

我还没有对此进行性能测试,但根据其他测试,它并不好。

要使Canvas快速:

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

如果你不记得在使用canvas时,你的游戏将会非常慢。这是我从经验中学到的。