【问题标题】:HTML5: Canvas performs too slow on lower end computersHTML5:Canvas 在低端计算机上执行速度太慢
【发布时间】:2011-07-01 03:38:01
【问题描述】:

我的问题是我的 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);
    }
}

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

【问题讨论】:

  • 我相信 Canvas 实现使用后台缓冲区,因此您实际上不会通过隐藏的 canvas 对象获得任何东西。
  • “有些电脑很慢,我该怎么办?” a) 尽你所能使你的代码更有效率,b) 接受你所尝试的限制去做。到目前为止,您对以下答案的回答表明您不愿意执行上述任何一项。
  • 先从简单开始。编写一个for 循环,它只循环通过i 并执行putImageData。那有多快?如果它不够快,那么 putImageData 告诉你什么?

标签: javascript optimization html canvas performance


【解决方案1】:

您指定的超时时间为 1 毫秒。没有浏览器可以如此快速地更新画布。将其更改为 1000 - 这将是 1 秒,即:

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

UPD。要尝试的另一件事是准备与动画中的帧数一样多的画布,将它们全部设置为display:none,然后依次打开display:block。我怀疑它会比 putImageData 更快,但仍然值得一试。

【讨论】:

  • 我知道,但是我增加的越多,动画就越慢(显然)。我想;在 1 时,它会尽可能快地尝试,但仍然太慢。
  • 嗯.. 动画速度越快,处理器的负载就越大。尝试找到最佳超时。 20 到 100 毫秒听起来很合理。
  • 速度很慢,因为您将其设置为 1 毫秒。尝试将其设置在 10 或 20 毫秒(甚至更高)之间,以在性能和动画流畅度之间找到平衡。
  • 再次强调,增加超时时间并不会提高动画速度。
  • @abloddywar;您错过了重点 - 无论您尝试做什么、更新画布或进行简单计算 - 每个 setTimeout 调用都会创建一个新的调用堆栈 - 无论如何尝试每毫秒都一定会很慢。你必须改变你的方法,否则你永远不会得到你喜欢的结果。
【解决方案2】:
  1. 如前所述,间隔为 1 毫秒的超时注定会失败,因此第一步就是停止它。

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

  3. 看起来您正在尝试在动画的每一步重新绘制整个 canvas - 这不是最佳的,请尝试仅处理发生变化的像素。您提供的“更具挑战性的画布脚本”的链接实际上比您尝试做的要简单得多,因为它都是基于矢量的数学 - 这是 canvas 元素的优化目标 - 它从来没有做过每 x 毫秒完全重新渲染一次,而且很可能永远不会。

  4. 1234563原子。

【讨论】:

  • 1.显然我不是从那个开始的,但它确实被证明是最好的表现。 2. 没有区别,实际上我从未见过这种方法。 3. 每次玩家移动时,canvascape(3d 'fps' 游戏)确实有一个 clearRect,这不是让它重绘整个画布吗? 4. 我画了一个形状,它需要有动态的比例。
  • 多次超时有点奇怪,很难维护。我建议使用 20ms 的 tomeout(以获得 50 fps)并且只调用一个全局函数来管理所有动画。
  • @UVL:我同意,setInterval 是大多数情况下的方法。
【解决方案3】:
  1. 如果可以,请不要使用putImageData。 FF3.6 上的表现is abysmal:

    (来源:phrogz.net

在屏幕外的画布上使用绘图命令,并使用drawImage 将精灵blit 到子区域。

  1. 正如@MartinJespersen 所说,重写你的画框循环:

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

  3. 使用较小的画布尺寸。如果你觉得够用,你甚至可以使用 CSS 扩大规模。

  4. 接受慢速计算机很慢的事实,并且您站在许多抽象层的肩膀上。如果您想为低端计算机争取性能,请使用 C++ 和 OpenGL 编写。否则,设置最低系统要求。

【讨论】:

    【解决方案4】:

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

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

    【讨论】:

      【解决方案5】:

      好的,第一件事。当您制作此动画时,else 发生了什么?任何其他javascript、任何其他计时器、任何其他处理程序?顺便说一句,答案不可能是什么都没有。您的浏览器正在重新绘制窗口 - 至少您正在更改的部分。如果其他 javascript 正在“运行”,请记住,严格来说这不是真的。 Javascript 在设计上是单线程的。您只能排队等待执行,因此如果其他一些 javascript 占用了线程,您将无法查看。

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

      第三,知道你在用function(){animate(i+1);}做什么。该匿名函数只能存在于其父级范围内。换句话说,当您将这样的函数排队时,父作用域仍然存在于调用堆栈中,正如@MartinJespersen 指出的那样。而且由于该函数将另一个、另一个和另一个排队……每个都将逐渐变慢。

      我把讨论的所有内容都放在了一个小小提琴中:

      http://jsfiddle.net/KzGRT/

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

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

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

      【讨论】:

        【解决方案6】:

        我以这种方式完成了 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);
            }
        };
        

        【讨论】:

          猜你喜欢
          • 2013-04-12
          • 2020-02-22
          • 2011-06-27
          • 2013-09-22
          • 1970-01-01
          • 2013-04-15
          • 2021-11-17
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多