Search code examples
javascriptinternet-explorer-7excanvas

IE7 excanvas drawImage workaround?


Ahoy StackOverflow!

I've run into the following problem: on IE7 only, if I call drawImage() after drawing anything to the canvas, excanvas does not draw the image at the desired x,y coordinates. I have looked on the excanvas project page/ google group, and found that there are known issues with the drawImage() function. ( for example: http://code.google.com/p/explorercanvas/issues/detail?id=104&q=IE7 )

I have tried restoring the identity matrix, as suggested here: Excanvas vml positioning issue , though it appears to have had no effect.

Attached is a simple html document demonstrating this issue:

<!DOCTYPE HTML>
<html>
<head>
    <script type="text/javascript" src="excanvas.js"></script>
    <script type="text/javascript">
        window.onload = function(){
            var icon, ctx;

            icon        = new Image();
            icon.width  = "20";
            icon.height = "20";
            icon.src    = "http://www.shangalulu.com/resources/images/icons/small/30.png";
            icon.onload = function(){
                var ctx;

                ctx = document.getElementById('the_canvas').getContext('2d');



                ctx.beginPath();
                ctx.moveTo(0,0);
                ctx.lineTo(500,0);
                ctx.lineTo(500,500);
                ctx.lineTo(0,500);
                ctx.lineTo(0,0);
                ctx.moveTo(200,200);
                ctx.lineTo(300,200);
                ctx.lineTo(300,300);
                ctx.lineTo(200,300);
                ctx.lineTo(200,200);
                ctx.moveTo(0,0);
                ctx.lineTo(500,500);
                ctx.moveTo(0,500);
                ctx.lineTo(500,0);
                ctx.stroke();


                ctx.drawImage(icon, 190, 190, 20, 20);
                ctx.drawImage(icon, 240, 240, 20, 20);
                ctx.drawImage(icon, 290, 290, 20, 20);

                return;
            }
        }
    </script>

</head>
<body>
    <canvas id="the_canvas" width="500" height="500"></canvas>
</body>
</html>

If you need the excanvas library to run this, you can grab it from here: http://code.google.com/p/explorercanvas/

The above script should do the following:

  1. it should draw an outline of the outer edges of the canvas (so you can see it)
  2. it should draw a 100x100 rectangle in the middle of the canvas.
  3. it should draw two lines that cross through the center of the canvas.
  4. it should draw three images: one on the top left corner of the inner box, one at the center, and one on the bottom right corner of the inner box.

What I would like to know is: Is there a known patch/workaround to this problem?


Solution

  • I've been pulling my hair on that one too...

    I was only able to find a workaround, which works fine as long as you do not apply transformations before drawing your image. It implies that you replace the function drawImage in excanvas.js with that one:

    contextPrototype.drawImage = function(image, var_args) {
    var dx, dy, dw, dh, sx, sy, sw, sh;
    
    // to find the original width we overide the width and height
    var oldRuntimeWidth = image.runtimeStyle.width;
    var oldRuntimeHeight = image.runtimeStyle.height;
    image.runtimeStyle.width = 'auto';
    image.runtimeStyle.height = 'auto';
    
    // get the original size
    var w = image.width;
    var h = image.height;
    
    // and remove overides
    image.runtimeStyle.width = oldRuntimeWidth;
    image.runtimeStyle.height = oldRuntimeHeight;
    
    if (arguments.length == 3) {
      dx = arguments[1];
      dy = arguments[2];
      sx = sy = 0;
      sw = dw = w;
      sh = dh = h;
    } else if (arguments.length == 5) {
      dx = arguments[1];
      dy = arguments[2];
      dw = arguments[3];
      dh = arguments[4];
      sx = sy = 0;
      sw = w;
      sh = h;
    } else if (arguments.length == 9) {
      sx = arguments[1];
      sy = arguments[2];
      sw = arguments[3];
      sh = arguments[4];
      dx = arguments[5];
      dy = arguments[6];
      dw = arguments[7];
      dh = arguments[8];
    } else {
      throw Error('Invalid number of arguments');
    }
    /////////////////////////// MODIFIED BIT ///////////////////////////
    var vmlStr = [];
    vmlStr.push('<g_vml_:image src="', image.src, '"',
                ' style="position:absolute;',
                ' top:', dy, 'px;',
                ' left:', dx, 'px;',
                ' width:', dw, 'px;',
                ' height:', dh, 'px;"',
                ' cropleft="', sx / w, '"',
                ' croptop="', sy / h, '"',
                ' cropright="', (w - sx - sw) / w, '"',
                ' cropbottom="', (h - sy - sh) / h, '"',
                ' />');
    /////////////////////////// END OF MODIFIED BIT ///////////////////////////
    
    this.element_.insertAdjacentHTML('BeforeEnd',
                                    vmlStr.join(''));
    };
    

    What it does is simply create a VML image directly inside the pseudo canvas, instead of wrapping it inside a group as it was originally done. For some reason, the original code was causing a left and top padding to appear around the image, and can't figure out why. Until we can understand the reason for that, we won't be able to find a proper fix...