为什么putImageData这么慢?

Dan*_*lig 37 performance html5 canvas

我正在使用一个相对较大的Canvas,其中绘制了各种(复杂)的东西.然后我想保存Canvas的状态,所以我可以快速将其重置为现在的状态.我为此使用getImageData并将数据存储在变量中.然后我将更多东西绘制到画布上,稍后将使用putImageData将Canvas重置为保存状态时的状态.

但事实证明,putImageData非常慢.事实上,它比简单地从头开始重绘整个Canvas要慢,这涉及覆盖大部分表面的几个drawImage,以及超过40.000行的操作,然后是笔画和填充.

从头开始重绘大约2000 x 5000像素的画布需要大约170毫秒,使用putImageData虽然需要高达240毫秒.与重绘画布相比,为什么putImageData这么慢,尽管重绘画布包括用drawImage填充几乎整个画布,然后使用lineTo,stroke和fill再次填充大约50%的画布和多边形.所以基本上每个像素在重绘时至少触摸一次.

因为drawImage似乎比putImageData快得多(毕竟,重绘画布的drawImage部分需要不到30毫秒).我决定尝试不使用getImageData来保存画布的状态,而是使用canvas.toDataURL,然后从数据URL创建一个Image,我将坚持使用drawImage将其绘制到画布.事实证明,整个过程要快得多,只需要大约35毫秒即可完成.

那么为什么putImageData比替代方案慢得多(使用getDataURL或只是重绘)?我怎么能进一步加快速度呢?有没有,如果,通常什么是存储画布状态的最佳方式?

(所有数字都是使用Firefox中的Firebug测量的)

Dan*_*lig 77

只是一个小的更新,最好的方法是做到这一点.我实际上是在高性能ECMAScript和HTML5 Canvas(pdf,德语)上写了我的学士论文,所以我现在收集了一些关于这个主题的专业知识.最明显的解决方案是使用多个画布元素.从一个画布到另一个画布的绘制速度与将任意图像绘制到画布一样快.因此,"存储"画布的状态与使用两个画布元素时再次恢复它一样快.

这个jsPerf测试用例非常清楚地显示了各种方法及其优点和缺点.

为了完整起见,在这里你应该如何做到:

// setup
var buffer = document.createElement('canvas');
buffer.width = canvas.width;
buffer.height = canvas.height;


// save
buffer.getContext('2d').drawImage(canvas, 0, 0);

// restore
canvas.getContext('2d').drawImage(buffer, 0, 0);
Run Code Online (Sandbox Code Playgroud)

根据浏览器的不同,此解决方案比获得upvotes的解决方案快5000倍.

  • +1对于伟大的信息和梦幻般的测试用例 (5认同)
  • 如果你需要在一个数组中存储很多状态怎么办?应该创建一堆画布的数组吗?例如`var numBuffers = 20; var tmpCan = document.createElement('canvas'); var 缓冲区 = [tmpCan]; for (var i = 1, len = numBuffers, i < numBuffers; i++) { buffers.push(tmpCan.cloneNode()); }`或类似的东西??**或** 有更好的解决方案吗? (2认同)
  • @JosephQuinsey jsperf.com 似乎有问题。我不确定这是间歇性的还是该网站已经永久停止存在。您也可以在上面链接的学士论文中找到我用于这些性能测试的实际源代码。附录中的最后两页包含 getImageData/putImageData 以及中间画布方法的源代码。 (2认同)

Cor*_*ger 11

在Firefox 3.6.8中,我能够通过使用toDataUrl/drawImage来解决putImageData的缓慢问题.对我而言,它的工作速度足够快,我可以在处理mousemove事件时调用它:

要保存:

savedImage = new Image()
savedImage.src = canvas.toDataURL("image/png")
Run Code Online (Sandbox Code Playgroud)

要恢复:

ctx = canvas.getContext('2d')
ctx.drawImage(savedImage,0,0)
Run Code Online (Sandbox Code Playgroud)

  • 刚刚意识到这个答案不断上升.我很欣赏这个答案,但解释的解决方案实际上是非常糟糕的.请改为参考我自己接受的答案. (3认同)