If you create canvas elements dynamically either for off-screen composition or sprite manipulation, when this is in an animation loop running hundreds of times a second it can start to leak memory.
I’m not entirely sure why this is; but it’s quite widespread in many browsers including Webkit. Perhaps the animation causes the browser to register as continually active which prevents the garbage collector from running – as it will clean it up if you stop the animation and change DOM elements e.g. from an Ajax update. But that’s just wild speculation…
What we are interested in is how to fix it, and also achieve some performance gains from not continuously creating DOM elements.
The trick is to create a function which caches these canvas elements and reuses them. The easiest way is to have a create canvas function that handles all this for you. To keep a handle on the different canvases in use in Illyriad, we use a named canvas element to ensure the correct one is returned. You can call them whatever is relevant:
[code lang=”javascript” title=”Create cached canvas”]
var CachedCanvases = new Object(); // Canvas element cache
function CreateCanvasCached(name) {
if (!CachedCanvases[name]) {
var canvas = document.createElement(‘canvas’);
CachedCanvases[name] = canvas;
return canvas;
}
return CachedCanvases[name];
};
[/code]
Using this is very simple, whenever you create a canvas dynamically that is just used for composition just (i.e. you won’t attach it to the DOM) use the cache function:
[code lang=”javascript”]
var SpriteCompositionCanvas = CreateCanvasCached("sprite");
SpriteCompositionCanvas.width = 64; SpriteCompositionCanvas.height = 64;
var SpriteCompositionContext = SpriteCompositionCanvas.getContext("2d");
…
[/code]
I’ll do another post on some of the wonderful composition operations you can do using canvas; when this technique will become very important.
If you are updating your page using Ajax and programmatically adding and removing the display of canvas elements you will want to clear the canvas cache when you change page by adding a cache clean up to your ajax page unload function.
[code lang=”javascript”]
function CleanupCachedCanvasses() {
var delList = new Array();
for (name in CachedCanvases) {
delList.push(name);
}
for (i = 0; i < delList.length; i++) {
delete CachedCanvases[delList[i]];
}
delete delList;
}
[/code]
Now you should be good to go!