Sunday, December 5, 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 = "http://3.bp.blogspot.com/_0sKGHtXHSes/TPt5KD-xQDI/AAAAAAAAA0s/udx3iWAzUeo/s1600/aspnethomepageplusdevtools.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.

<script type="text/javascript">        
    window.addEventListener("load", draw, true);
    
    function draw(){                                    
        var img = new Image();
        img.src = "http://3.bp.blogspot.com/_0sKGHtXHSes/TPt5KD-xQDI/AAAAAAAAA0s/udx3iWAzUeo/s1600/aspnethomepageplusdevtools.PNG";                
        img.onload = function(){
            var canvas = document.getElementById('canvas');
            var context = canvas.getContext('2d');    
        
            context.drawImage(img, 0, 0);        
        };            
    }                    
</script>

You can find the demo and source here.

Did you spot the problem in the first codesnippet?

9 comments:

  1. Yeah, I also stumbled across that one.
    What I would have expected even less was that this sometimes even happens when using a "data:..." URL as the image source!

    ReplyDelete
  2. Not all browsers fire the onload-event when the image is loaded from cache.

    ReplyDelete
  3. Thanks for the tip. Found a good discussion on StackOverflow (http://stackoverflow.com/questions/1038327/image-onload-not-firing-twice-in-ie7).

    ReplyDelete
  4. This comment has been removed by the author.

    ReplyDelete
  5. I am trying to call a webpage on my canvas on which I can draw or add notes. Now I want to take a screenshot where the drawing is saved with the webpage as background.
    I called a url in iframe behind the canvas (using z-index)
    I can save only the image which I draw on canvas and not the webpage which I called.
    Please help how I can get it to work.
    Is there any way I can call a url within my canvas?

    ReplyDelete
  6. I don't think you can tbh. Maybe you should ask the question on StackOverflow?

    ReplyDelete
  7. congrats! keep up the good work/this is a great presentation.
    canvas Print

    ReplyDelete
  8. Just wanted to let you know that this was EXTREMELY helpful. Thank you very much.

    ReplyDelete