Using the drawimage() function to increase your canvas performance - caching draw commands on an offscreen canvas

Written by Richard Heyes, on 23rd February 2014

An explanation of how you can 'cache' your canvas on a temporary canvas and use the drawImage() function to copy it back when you need to to eliminate repeated drawing

Right off of the bat I'll admit - canvas is fast! All it is is a bunch of pixels - no costly DOM or state mechanism to maintain. All of the drawing is done on a "fire-and-forget" basis. This means that once you draw something on to the canvas you need to manually remember the coordinates of it if you want to draw over it again (draw a different coloured square over an existing square for example) - and a partial redraw of the canvas - or even a whole redraw will be necessary if you have to change something (or if your square is a semi-transparent colour).

This repeated drawing can take its toll on performance. A Bar chart that is animated using the RGraph Wave effect for example can be quite slow - the Wave effect is quite intensive and can be slowed if you use a background grid. So for this reason the background is now cached on an offscreen canvas and once drawn the offscreen canvas is copied over to the destination canvas instead of the background being repeatedly drawn. It's copied over by first drawing the grid on the off-screen canvas and then using the drawImage() function on the visible canvas but instead of an image object as the first rgument - you give it the off-screen canvas instead. Like this:

    // Draw the background grid (assume that this code is running inside a Bar chart object - so the this
    // variable refers to the object)
    if (this.cachedBackgroundCanvas && cacheEnabled) {
        // Don't bother drawing the background - simply copy the cached canvas over from the other canvas
        // The image is placed at -0.5,-0.5 because of RGraphs translation (for antialiasing purposes).
        // If you don't do this then you would use 0,0 as coordinates.
    } else {
        // Create the cached canvas
        this.cachedBackgroundCanvas        = document.createElement('CANVAS');
        this.cachedBackgroundCanvas.width  = canvas.width;
        this.cachedBackgroundCanvas.height = canvas.height;
        // Now draw the background on to the main canvas
        // Copy whats just been drawn from the main canvas on to the cached canvas

Whether this affects anything else remains to be seen - but since only the background draw is being cached it should be fine (famous last words?). An alternative would be to disable the background grid entirely - or at least the vertical grid lines.