5

I recently got about doing some HTML5/Canvas stuff and was going about my business quite happily, testing stuff in Chrome, until I decided to try what I have been working on in Firefox... doesn't work so good.

This is a bare bones example of the kind of stuff I'm doing. Setting up the basic requestAnimationFrame shim, the main loop clears the canvas and then updates and draws my objects. Easy enough, examples about this stuff are every where to be found.

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/

When I test that in Chrome it looks great, but Firefox has a lot of stuttering and tearing, for what seems a rather simple task.

I have found similar questions but none with a good clear solution. Is this a Firefox thing? Are Webkit browsers just better at doing this? Should I just give up on it and hope it is fixed in future versions of the browser? Or maybe it is my particular set up? I'm using Windows 7 64bit with FireFox 17.0.1.

Any help is appreciated.

diego
  • 51
  • 1
  • 4
  • This (The fiddle) seems to be running just fine in my Firefox. When have you last cleared the browser's cache / history, and do you have any other tabs running? In my experience, FF can "Bog up" a little. – Cerbrus Jan 07 '13 at 14:52
  • No other tabs running, I even disabled every plugin and extension. I guess it is something with my specific setup, I can live with that. It's annoying, but I can live with it :P – diego Jan 07 '13 at 15:11
  • I have noticed that on my desktop, FF can tend to "stutter" once every 2 seconds when I have a load of tabs open, this is especially noticeable when playing video's. Maybe the FF renders itself once every while, causing a slight delay. All speculation, though. – Cerbrus Jan 07 '13 at 15:13
  • I see stuttering in FF 20 (nightly) as well. I noticed worse canvas performance in FF in my demos as well (e.g. http://fabricjs.com/particles/) – kangax Jan 07 '13 at 22:44
  • While tweaking your JSFiddle, I think I stumbled on a less/no stuttering version: http://jsfiddle.net/hakanensari/K52Gd/2/ – Hakan Ensari Jan 13 '13 at 12:04
  • @HakanEnsari, can you say which of the changes you made helped? It looks like you changed the code, made the canvas smaller, and changed the behavior (making one rect move faster than the others). Unless it's just the smaller canvas, I can't tell why this would run smoother. – Darius Bacon May 12 '13 at 21:37
  • Firefox is (currently) less efficient compared to Chrome in most part when it comes to canvas. I have registered in some rare cases FF is faster. Optimize using all tricks in the book. This will also benefit not only Chrome users but mobile users too. –  Jul 16 '13 at 22:30

3 Answers3

1

The solution @HakanEnsari provided seems to work. I was curious about the reason and found that it's because his version of the code doesn't clear the entire canvas. It only clears the individual 10x10 Rects and leaves the rest of the canvas alone.

This goes into it a bit, and also has a lot of other useful canvas performance tips: http://www.html5rocks.com/en/tutorials/canvas/performance/#toc-render-diff

So you want this:

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

    requestAnimFrame(loop);

Just clear the individual rects

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

(tweaked fiddle: http://jsfiddle.net/shaunwest/B7z2d/1/)

Shaun West
  • 91
  • 2
  • If you remove the `closePath` in the fiddle (which is not needed here, rects are already closed) it will go even a tad faster. –  Jul 16 '13 at 22:28
0

Apparently, clearing the canvas with canvas.width = canvas.width; caused the lag on Safari (I'm browsing with version 5.1.9).

I've never used that way of clearing the screen: instead, I use this one:

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

If you try it out, it shouldn't lag anymore. See jsfiddle.


This is the fastest way to clear the canvas: on the contrary, clearing each individual element requires you to:

  1. keep track of every element position
  2. perform a clearRect call for every element you want to redraw

and also wouldn't work for shapes other than a rectangle (as there is no clearSphere or clearPath method).

Saturnix
  • 10,130
  • 17
  • 64
  • 120
  • 2
    `canvas.width = canvas.width` is a hack so no surprise there. The other claim is true in some cases, false in others. There is a threshold that kicks in. The cost is not keeping track but actually doing the clearing. Small clears uses less time than one big clear. Result can vary from machine to machine. –  Jul 16 '13 at 22:44
  • It also vary according to what you need to do. If you really just have to move 4 squares on a black background, go on and clear just those. On a more generic approach, any animation with a shape moving on a background requires full redraw (and since I don't understand the sense of doing that with `canvas.width = canvas.width` I posted the answer). – Saturnix Jul 16 '13 at 22:46
  • @KenFyrstenberg Although a non-optimized clearRect should be an oddity these days (read: it's faster than a cat blink) .. it is that *redrawing* smaller/dirty regions can be a much more favorable endeavor for increasing performance. – user2864740 Oct 23 '14 at 20:58
0

Another reason of the stutters is that until FireFox24, the animations of FireFox was not fully synchronized to the refresh rate (VSYNC), especially if the refresh rate was not exactly 60Hz.

It has to do with the end of section 5 of W3C recommendation, http://www.w3.org/TR/animation-timing/ for the browsers to synchronize animations to the refresh rate. It now runs almost equally as smoothly in both Chrome and FireFox on Windows, since FireFox 24.

TestUFO lists all the supported browsers (that can sync requestAnimationFrame() to the refresh rate) at http://www.testufo.com/browser.html

Mark Rejhon
  • 869
  • 7
  • 14