画布动画在 FireFox 中卡顿,但在 Chrome 中非常完美

Posted

技术标签:

【中文标题】画布动画在 FireFox 中卡顿,但在 Chrome 中非常完美【英文标题】:Canvas animation stutters in FireFox but is perfect in Chrome 【发布时间】:2012-12-21 07:36:36 【问题描述】:

我最近开始做一些 html5/Canvas 的东西,并且非常愉快地开展我的业务,​​在 Chrome 中测试东西,直到我决定尝试我在 Firefox 中所做的工作......效果不太好。

这是我正在做的那种事情的一个简单的例子。设置基本的 requestAnimationFrame 垫片,主循环清除画布,然后更新并绘制我的对象。很简单,关于这些东西的例子随处可见。

function loop() 
  canvas.width = canvas.width;

  requestAnimFrame(loop);

  rR.update();
  rG.update();
  rB.update();
  rY.update();

  rR.draw();
  rG.draw(); 
  rB.draw();
  rY.draw();


function Rect(color, x, y, speedX, speedY) 
  this.x = x;
  this.y = y;
  this.color = color;
  this.speedX = speedX;
  this.speedY = speedY;


Rect.prototype.draw = function () 
  context.fillStyle = this.color;
  context.beginPath();
  context.rect(this.x, this.y, 10, 10);
  context.closePath();
  context.fill();
;

Rect.prototype.update = function () 
  if (this.x < 0 || this.x > canvas.width) this.speedX = -this.speedX;
  if (this.y < 0 || this.y > canvas.height) this.speedY = -this.speedY;

  this.x += this.speedX;
  this.y += this.speedY;
;

var rR = new Rect("#FF0000", canvas.width/2, canvas.height/2, 2, 2);
var rG = new Rect("#00FF00", canvas.width/2, canvas.height/2, -2, -2);
var rB = new Rect("#0000FF", canvas.width/2, canvas.height/2, 2, -2); 
var rY = new Rect("#FFFF00", canvas.width/2, canvas.height/2, -2, 2);

http://jsfiddle.net/Polaris666/psDM9/3/

当我在 Chrome 中测试时,它看起来很棒,但 Firefox 有很多卡顿和撕裂,这似乎是一项相当简单的任务。

我发现了类似的问题,但没有一个明确的解决方案。这是火狐的事情吗? Webkit 浏览器在这方面做得更好吗?我应该放弃它并希望它在未来版本的浏览器中得到修复吗?或者也许这是我的特殊设置?我正在使用 Windows 7 64 位和 FireFox 17.0.1。

感谢任何帮助。

【问题讨论】:

这(小提琴)似乎在我的 Firefox 中运行良好。您最后一次清除浏览器的缓存/历史记录是什么时候,您是否有任何其他选项卡正在运行?根据我的经验,FF 可能会“陷入困境”。 没有其他选项卡在运行,我什至禁用了每个插件和扩展。我想这与我的特定设置有关,我可以忍受。这很烦人,但我可以忍受:P 我注意到,在我的桌面上,当我打开大量标签时,FF 会每 2 秒“卡顿”一次,这在播放视频时尤其明显。也许 FF 每隔一段时间就会渲染一次,从而导致轻微的延迟。不过都是猜测。 我在 FF 20(每晚)中也看到了口吃。在我的演示中,我也注意到 FF 中的画布性能更差(例如 fabricjs.com/particles) 在调整 JSFiddle 时,我想我偶然发现了一个更少/没有口吃的版本:jsfiddle.net/hakanensari/K52Gd/2 【参考方案1】:

@HakanEnsari 提供的解决方案似乎有效。我很好奇原因,发现是因为他的代码版本并没有清除整个画布。它只会清除单独的 10x10 矩形,而不会影响画布的其余部分。

这有点涉及,并且还有很多其他有用的画布性能提示: http://www.html5rocks.com/en/tutorials/canvas/performance/#toc-render-diff

所以你想要这个:

  function loop() 
    // get rid of this
    //canvas.width = canvas.width; 

    requestAnimFrame(loop);

只需清除单个矩形

Rect.prototype.update = function ()   
    if (this.x < 0 || this.x > canvas.width) this.speedX = -this.speedX;
    if (this.y < 0 || this.y > canvas.height) this.speedY = -this.speedY;

    // clear just the rect
    context.clearRect(this.x, this.y, 10, 10);

    this.x += this.speedX;
    this.y += this.speedY;
  ;

(调整小提琴:http://jsfiddle.net/shaunwest/B7z2d/1/)

【讨论】:

如果你删除小提琴中的closePath(这里不需要,rects 已经关闭)它会更快一点。【参考方案2】:

显然,使用canvas.width = canvas.width; 清除画布会导致 Safari 出现延迟(我正在使用 5.1.9 版浏览)。

我从来没有用过那种清屏方式:相反,我用的是这种方式:

context.clearRect(0,0, canvas.width, canvas.height);

如果您尝试一下,它应该不会再滞后了。见jsfiddle。


这是清除画布的最快方法:相反,清除每个单独的元素需要您:

    跟踪每个元素的位置 对要重绘的每个元素执行 clearRect 调用

也不适用于矩形以外的形状(因为没有 clearSphereclearPath 方法)。

【讨论】:

canvas.width = canvas.width 是一个 hack,所以这并不奇怪。另一种说法在某些情况下是正确的,在其他情况下是错误的。有一个门槛开始了。成本不是跟踪,而是实际进行清算。小清除比一个大清除使用更少的时间。结果可能因机器而异。 它也会根据您需要做的事情而有所不同。如果您真的只需要在黑色背景上移动 4 个方格,请继续清除这些方格。在更通用的方法中,任何形状在背景上移动的动画都需要完全重绘(因为我不明白用canvas.width = canvas.width 这样做的意义我发布了答案)。 @KenFyrstenberg 尽管这些天未优化的 clearRect 应该是一个奇怪的东西(阅读:它比猫眨眼更快).. 它是 重绘 较小/肮脏的区域可以为提高性能做出更有利的努力。【参考方案3】:

卡顿的另一个原因是在 FireFox24 之前,FireFox 的动画并未与刷新率 (VSYNC) 完全同步,尤其是在刷新率不完全为 60Hz 的情况下。

这与 W3C 建议第 5 节的结尾有关,http://www.w3.org/TR/animation-timing/ 让浏览器将动画与刷新率同步。自 FireFox 24 以来,它现在在 Windows 上的 Chrome 和 FireFox 中运行几乎同样流畅。

TestUFO 列出了所有支持的浏览器(可以将 requestAnimationFrame() 同步到刷新率)http://www.testufo.com/browser.html

【讨论】:

以上是关于画布动画在 FireFox 中卡顿,但在 Chrome 中非常完美的主要内容,如果未能解决你的问题,请参考以下文章

html画布不会填满firefox mobile上的整个页面

Firefox 中的 requestAnimationFrame 闪烁问题

CSS 动画在 chrome 中有效,但在 Firefox 和 Safari 中无效

jQuery UI 切换类动画在 Safari / Chrome 中没有动画 - 但在 Firefox 中动画?

SVG 中风动画颜色在 Firefox 中正确渲染,但在 Chrome 中失败

为啥这个 SVG 蒙版动画在 Firefox 中断断续续,但在 Chrome 中却很流畅?