HTML5:Canvas在低端计算机上执行速度太慢

时间:2011-02-21 09:17:06

标签: javascript optimization html5 canvas performance

我的问题是我的javascript / canvas在低端计算机上运行速度非常慢(即使它们可以顺利运行更具挑战性的canvas scripts。 我正在尝试根据用户选择做一个简单的动画。

当直接在画布上绘图被证明太慢时,我在隐藏的画布上绘制并将所有帧(getImageData)保存到data,然后调用animate(1);来绘制我的画布真实的画布。

function animate(i){
    if(i < 12){
        ctx2.putImageData(data[i], 0, 0);
        setTimeout(function(){animate(i+1)},1);
    }
}

但即使这样太慢了。我该怎么办?

6 个答案:

答案 0 :(得分:7)

  1. 如果可以提供帮助,请不要使用putImageData。 FF3.6上的表现is abysmal
    Test results showing poor getImageData performance http://phrogz.net/tmp/canvas_copy_benchmark.png
    使用drawImage在屏幕外画布和blit精灵上使用绘图命令代替子区域。

  2. 正如@MartinJespersen所提到的,重写你的框架绘图循环:

    var animate = function(){
      // ...
      setTimeout(animate,30); //Max out around 30fps
    };
    animate();
    
  3. 如果您使用的库强制每帧clearRect,但您不需要,请停止使用该库。清除并重新绘制您需要的部分。

  4. 使用较小的画布尺寸。如果你觉得它足够了,你甚至可以用CSS扩展它。

  5. 接受缓慢的计算机是慢的,你站在许多抽象层的肩膀上。如果您想要了解低端计算机的性能,请使用C ++和OpenGL编写。否则,请设置最低系统要求。

答案 1 :(得分:3)

您指定的超时为1毫秒。没有浏览器可以快速更新画布。把它改为1000 - 那将是1秒,即:

setTimeout(function(){animate(i+1)}, 1000)

UPD。要尝试的另一件事是准备尽可能多的画布,因为动画中有帧,将所有帧设置为display:none,然后在它们上转display:block顺序。我怀疑它会比putImageData更快,但仍值得尝试。

答案 2 :(得分:2)

  1. 如前所述,间隔为1毫秒的超时注定会失败,所以第一步就是停止。

  2. 您正在调用setTimeout recursivly,这对于创建动画并不理想。而是同时启动整个动画所需的所有setTimeout,同时增加循环中的延迟并让它们运行,或者更好地使用setInterval这是做动画的更好方法,以及jQuery的动画如何工作。

  3. 看起来您正试图在动画的每一步重绘整个canvas - 这不是最佳选择,只尝试操纵更改的像素。你给“更多challanging canvas脚本”的链接实际上比你想做的更简单,因为它是所有基于矢量的数学 - 这是canvas元素被优化的 - 它从未被制作过每隔x毫秒进行一次完全重新渲染,很可能永远不会。

  4. 如果你真正需要做的是改变动画中每一帧的整个图像 - 不要使用canvas,而是使用带有预加载图像的普通图像标签,那么它将在ie6上顺利运行单数原子。

答案 3 :(得分:0)

我有一个类似谷歌地图的应用程序 - 它可以让您点击并平移大图像。我重绘了我的画布,从每个重绘的大图像中采样和缩放。

无论如何,我碰巧尝试了双画布方法 - 在需要时绘制到(更大)缓冲区,然后执行canvas_display.drawImage(canvas_buffer)以将区域输出到屏幕。我不仅没有看到性能提升,而且iPhone的速度明显变慢。只是一个数据点...

答案 4 :(得分:0)

好的,首先要做的事情。你正在做这个动画时会发生什么 else ?任何其他JavaScript,任何其他计时器,任何其他处理程序?顺便说一句,答案不能是 nothing 。您的浏览器正在重新绘制窗口 - 至少是您要更改的位。如果其他javascript正在“运行”,请记住,这不是严格正确的。 Javascript是单线程设计。你只能排队执行,所以如果其他一些javascript占用线程,你就不会看到。

其次,了解计时器的工作原理。 http://ejohn.org/blog/how-javascript-timers-work/是我个人最喜欢的帖子。特别是,setTimeout要求浏览器在至少指定时间之后运行某些内容,但仅在浏览器有开放时才会运行。

第三,知道你在function(){animate(i+1);}做了些什么。该匿名函数只能 存在于其父级范围内。换句话说,当你排队这样的函数时,父范围仍然存在于callstack上,正如@MartinJespersen指出的那样。而且,由于该功能排队另一个,另一个,另一个......每个都会逐渐变慢。

我把所讨论的所有内容放在一个小小的地方:

http://jsfiddle.net/KzGRT/

(我第一次使用jsfiddle,所以要善待)。这是一个简单的10帧动画(名义上)为100毫秒,每个都使用setTimeout。 (我已经这样做而不是setInterval,因为理论上,执行时间较长的应该开始落后于其他人。理论上 - 再次,因为javascript是单一的 - 如果一个人放慢速度,它也会延迟其他人。)

top方法只在重叠的画布上绘制了所有十张图像,一次只显示一张。动画只是隐藏前一帧并显示下一帧。第二个将putImageData执行到具有顶级功能的画布中。第三个使用您尝试过的匿名函数。观察第0帧的红色闪光,你会看到谁正在执行最快 - 对我来说,它需要一段时间,但它们最终开始漂移(在Chrome中,在一台体面的机器上。在FF中应该更明显在较低规格的东西上。)

在您的低端测试机器上试一试,看看会发生什么。

答案 5 :(得分:0)

我以这种方式完成了setTimeout,希望它可以帮助某人提升应用程序:

var do = true;
var last = false;
window.onmousemove = function(evt){
    E.x = evt.pageX - cvs.offsetLeft;
    E.y = evt.pageY - cvs.offsetTop;
    if(do){
        draw();
        do = false;
        //in 23 ms drawing enabled again
        var t = setTimeout(function(){do = true;},23);
    }else{
        //the last operation must be done to catch the cursor point
        clearTimeout(last );
        last = setTimeout(function(){draw();},23);
    }
};