Using the drawimage() function to increase your canvas performance - caching draw commands on an off-screen 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 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 colored 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 color).

This repeated drawing can take its toll on performance. A Bar chart that is animated using the 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 off-screen canvas and once drawn the off-screen 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 argument - you give it the off-screen canvas instead. Like this:

<script>
    // 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 anti-aliasing purposes).
        // If you don't do this then you would use 0,0 as coordinates.
        context.drawImage(this.cachedBackgroundCanvas,-0.5,-0.5);
    
    } 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
        RGraph.background.Draw(this);
        
        // Copy what's just been drawn from the main canvas on to the cached canvas
        this.cachedBackgroundCanvas.getContext('2d').drawImage(ca,0,0);
    }
</script>

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.