DO YOU KNOW SOME MORE BEST PRACTICES FOR CANVAS??
Please add to this thread what you know, have learned, or have read online any and all Canvas best practices, tips/tricks for performance
With Canvas being still very new to internet, and no signs of it ever getting old that I can see in the future, there are not too many documented "best practices" or other really important tips that are a 'must know' for developing with it in any one particular place. Things like this are scattered around and many times on lesser known sites.
There's so many things that people need to know about, and still so much to learn about too.
I wanted to share some things to help people who are learning Canvas and maybe some who already know it quite well and am hoping to get some feedback from others about what they feel are some best practices or other tips and tricks for working with Canvas in HTML5.
I want to start off with one I personally found to be quite a useful yet surprisingly uncommon thing for developers to do.
1. Indent your code
Just as you would any other time, in any other language whatever the case may be. It has been a best practice for everything else, and I have come to find that in a complex canvas app, things can get a little confusing when dealing with several different contexts and saved/restore states. Not to mention the code is just more readable and overall cleaner looking too.
For example:
...
// Try to tell me this doesn't make sense to do
ctx.fillStyle = 'red';
ctx.fill();
ctx.save();
if (thing < 3) {
// indenting
ctx.beginPath();
ctx.arc(2, 6, 11, 0, Math.PI*2, true);
ctx.closePath();
ctx.beginPath();
ctx.moveTo(20, 40);
ctx.lineTo(10, 200);
ctx.moveTo(20, 40);
ctx.lineTo(100, 40);
ctx.closePath();
ctx.save();
ctx.fillStyle = 'blue'
ctx.fill();
ctx.restore();
} else {
// no indenting
ctx.drawImage(img, 0, 0, 200, 200);
ctx.save();
ctx.shadowBlur();
ctx.beginPath();
ctx.arc(2, 60, 10, 0, Math.PI*2, false);
ctx.closePath();
ctx.fillStyle 'green';
ctx.fill();
ctx.restore();
}
ctx.restore();
ctx.drawRect();
ctx.fill();
...
Is the IF statement not easier and cleaner to read and know what is what immediately going on than the ELSE statement in this? Can you see what I'm saying here? I think this should be a method that developers should continue to practice just as they would when writing plain 'ol javascript or any other language even.
Use requestAnimationFrame instead of setInterval / setTimeout
setInterval and setTimeout were never intended to be used as animation timers, they're just generic methods for calling functions after a time delay. If you set an interval for 20ms in the future, but your queue of functions takes longer than that to execute, your timer won't fire until after these functions have completed. That could be a while, which isn't ideal where animation is concerned. RequestAnimationFrame is a method which tells the browser that an animation is taking place, so it can optimize repaints accordingly. It also throttles the animation for inactive tabs, so it won't kill your mobile device's battery if you leave it open in the background.
Nicholas Zakas wrote a hugely detailed and informative article about requestAnimationFrame on his blog which is well worth reading. If you want some hard and fast implementation instructions, then Paul Irish has written a requestAnimationFrame shim – this is what I've used in every one of the Canvas apps I have made until recently.
ACTUALLY
Even better than using requestAnimationFrame in place of setTimeout and setInterval, Joe Lambert has written a NEW and improved shim called requestInterval and requestTimeout, which he explains what issues exist when using requestAnimFrame. You can view the gist of the script.
ACTUALLY x2
Now that all the browsers have caught up on the spec for this, there has been an update to the requestAnimFrame() polyfill, one which will probably remain the one to use to cover all vendors.
Use more than one canvas
A technique for animation-heavy games which @nicolahibbert wrote about in a post of hers on optimizing Canvas games mentions that it may be better to use multiple canvasses layered on top of one another rather than do everything in a single canvas. Nicola explains that "drawing too many pixels to the same canvas at the same time will cause your frame rate to fall through the floor. Take Breakout for example. Trying to draw the bricks, the ball, the paddle, any power-ups or weapons, and then each star in the background – this simply won't work, it takes too long to execute each of these instructions in turn. By splitting the starfield and the rest of the game onto separate canvases, you are able to ensure a decent framerate."
Render Elements Off-screen
I have had to do this for a few apps I've made including Samsung's Olympic Genome Project facebook app. It's an extremely useful thing to know and to make use of whether it's needed or not. It decreases load time immensely, plus it can be a really useful technique to load images off screen since they can sometimes take a while.
var tmpCanvas = document.createElement('canvas'),
tmpCtx = tmpCanvas.getContext('2d'),
img = document.createElement('img');
img.onload = function() {
tmpCtx.drawImage(thumbImg, 0, 0, 200, 200);
};
img.src = '/some/image/source.png';
Notice that the src of the image is set after it is loaded. This is a key thing to remember to do too. Once the images are done loading and drawn into these temp canvases, you can then draw them to your main canvas by using the same ctx.drawImage(), but instead of putting the image as the first argument, you use 'tmpCtx.canvas' to reference the temp canvas.
Other tips, tricks and resources
- Canvas test cases
- Some more canvas and JS tests
- HTML5Rocks performance Improving
- **requestAnimFrame to Optimize Dragging Events
Canvas has a back-reference
The 2d context has a back reference to it's associated DOM element:
var ctx = doc.getElementById('canvas').getContext('2d');
console.log(ctx.canvas); // HTMLCanvasElement
I'd love to hear more from other people on this. I am working on making a list of things that we should standardize to add a new section to my company's Front-end Code Standards and Best Practices. I'd love to get as much feedback on this as I can.