Jef Claes

On software and life

05 Dec 2010

HTML5: Drawing images to the canvas gotcha

While I was playing with the Canvas API I came across a weird issue: I was trying to draw an image to the canvas, but the image failed to render very often.

Have a look at the source. Do you spot the problem?

<!DOCTYPE html>
<html>
    <head>
        <title>HTML5: Canvas</title>
        <script type="text/javascript">        
            window.addEventListener("load", draw, true);
            
            function draw(){                            
                var canvas = document.getElementById('canvas');
                var context = canvas.getContext('2d');    
 
                var img = new Image();
                img.src = "logo.png";                

                context.drawImage(img, 0, 0);                
            }            
        </script>        
    </head>

    <body>
        <canvas id="canvas" height="500" width="500">
            Looks like canvas isn't supported in your browser! 
        </canvas>
    </body>
</html>

It wasn’t until I opened Firebug and saw an unhandled exception in the console that I discovered what was going on.

uncaught exception: \[Exception... "Component returned failure code:
0x80040111 (NS\_ERROR\_NOT\_AVAILABLE)
\[nsIDOMCanvasRenderingContext2D.drawImage\]" nsresult: "0x80040111
(NS\_ERROR\_NOT\_AVAILABLE)" location: "JS frame :: file:///....html
:: draw :: line 15" data: no\]

Browsers load images asynchronously while scripts are already being interpreted and executed. If the image isn’t fully loaded the canvas fails to render it. Turns out the weird issue, is pretty logical.

Luckily this isn’t hard to resolve. We just have to wait to start drawing until we receive a callback from the image, notifying loading has completed.

window.addEventListener("load", draw, true);

function draw(){                                    
    var img = new Image();
    img.src = "logo.png";                

    img.onload = function(){
        var canvas = document.getElementById('canvas');
        var context = canvas.getContext('2d');    

        context.drawImage(img, 0, 0);        
    };            
}                    

Et voila!