Easy double buffering on HTML5 canvas
There is a lot of cool stuff around HTML5 canvas, requestAnimationFrame and making animations with Javascript.
If you can, you should also check out CSS animations if you're after animating DOM elements.
For more advanced stuff, you may want to use a canvas, that is usually GPU-accelerated and allows pretty high and stable framerates using window.requestAnimationFrame
.
If you need double buffering on the canvas, one of the popular ways is to create a second canvas element and draw to that one and then draw the completed image to the primary canvas using drawImage, resulting in something like this:
var primaryCtx = document.getElementById("canvas").getContext("2d");
var secondaryCanvas = document.createElement("canvas"),
secondaryCtx = secondaryCanvas.getContext("2d");
(function drawFrame() {
requestAnimationFrame(drawFrame);
secondaryCtx.fillStyle = "#f00";
secondaryCtx.fillRect(10,10,20,20);
primaryCtx.drawImage(secondaryCanvas);
})();
which never felt quite right.
Enter ctx.save()
and ctx.restore()
Today I discovered there's a way that feels cleaner and performs just as good as the method from above:
(function drawFrame() {
requestAnimationFrame(drawFrame);
primaryCtx.save(); //Freeze redraw
primaryCtx.fillStyle = "#f00";
primaryCtx.fillRect(10,10,20,20);
primaryCtx.restore(); //And now do the redraw
})();
which, despite the weird names, just freezes the rendering of the context and then resumes rendering after we're finished drawing on it.
Written by Martin Naumann
Related protips
2 Responses
Hi, nice try. But I fear that using save() and restore() does not have your described effect. save() does not save the canvas (or its content) but its state, like e.g transformation matrices and drawing colors. While save() pushes the current state on a stack, restore() pops it back. This has nothing to do with double buffering.
Well, if it really reduced the flickering then think pragmatic and let it like this. Regarding the "double buffering" it seems that nowadays most browsers implement some double buffering. Take a look here: http://goo.gl/Ue05s7
Another very nice post on general Canvas performances is here: http://www.html5rocks.com/en/tutorials/canvas/performance/
One of the tips is about off-screen rendering to gain runtime performance. This is similar to classic double buffering (the author called it displaylist) and I make broad use of it. Not only for performance a important technique.
To effectively reduce flickering it is important to use requestAnimationFrame() instead of setInterval(). Here is some more detailed description, why to use requestAnimationFrame(): http://devbutze.blogspot.com.br/2013/09/requestanimationframe-is-what-you-need.html