Search code examples
javascripthtml5-canvas

Failed to execute 'createPattern' on 'CanvasRenderingContext2D': The image argument is a canvas element with a width or height of 0


I'm trying to make a loupe for a canvas project and I'm trying to make a pattern that involves copying part of the canvas that contains the image copy to a second, smaller, canvas. I keep running into a error stating:

"Failed to execute 'createPattern' on 'CanvasRenderingContext2D': The image argument is 
 a canvas element with a width or height of 0."

Here is an example of the code:

HTML:

<canvas id="tCanvas" width=240 height=240 style="background-color:aqua;">
</canvas>
<canvas id="canvas1" width=240 height=240 style="background-color:#808080;">
</canvas>
<p></p>
<button id="download"   onclick="magnify();">Zoom</button>

JS:

    var canvas = document.getElementById("canvas1");
    var ctx = canvas.getContext('2d')
    var base64 = canvas.toDataURL('image/png', 0);
    drawing = new Image();
    drawing.src = base64; // can also be a remote URL e.g. http://
    var canvas1 = document.getElementById("tCanvas");
    var ctx1 = canvas1.getContext('2d');
    ctx1.drawImage(drawing, 0, 0);
    let w = drawing.naturalWidth
    let h = drawing.naturalHeight
    let size = w / 4 // Size (radius) of magnifying glass
    let magnification = 2
    let r = size / magnification // Radius of part we want to magnify
    
    let px = w / 3.5
    let py = h / 4

     let tileCanvas = document.getElementById("tCanvas")
     tileCanvas.width = 2 * size
     tileCanvas.height = 2 * size
    
      tileCanvas.getContext('2d').drawImage(canvas, px - r, py - r, 2 * r, 2 * r, 0, 0, 2 * size, 2 * size)
    
      let pattern = ctx.createPattern(tileCanvas, "repeat")
    
      ctx.fillStyle = pattern
   
     ctx.translate(px - size, py - size)

    ctx.beginPath()
    ctx.arc(size, size, size, 0, 2 * Math.PI)
    ctx.fill()
    ctx.strokeStyle = "orangered"
    ctx.lineWidth = 12
    ctx.stroke()
};

Solution

  • This is a rather classic problem caused by the 'asynchronous nature' of image loading. Let's take a look at the error message:

    "Failed to execute 'createPattern' on 'CanvasRenderingContext2D': The image argument is a canvas element with a width or height of 0."

    It says that either the height or the width of the object sent to the createPattern() method is zero but why should that happen?

    Let's step back in your code a little.

    The object in question is tileCanvas and it's width and height is determined here:

     tileCanvas.width = 2 * size
     tileCanvas.height = 2 * size
    

    So what's the value of size? Stepping back a bit further reveals

    let size = w / 4
    

    and w in turn is

    let w = drawing.naturalWidth
    

    which is the crux of the matter. naturalWidth is a property of an image object - drawing in your case. The problem is that you're calling it right after populating it's .src property. At this point in time the image might not have been completely loaded, thus it returns zero.

    You must wait for the image to be fully loaded until querying it's properties. This is done by listening to the onload event.

    Here's an example:

    var canvas = document.getElementById("canvas1");
    var ctx = canvas.getContext('2d')
    var base64 = canvas.toDataURL('image/png', 0);
    drawing = new Image();
    drawing.onload = () => {
      var canvas1 = document.getElementById("tCanvas");
      var ctx1 = canvas1.getContext('2d');
      ctx1.drawImage(drawing, 0, 0);
      let w = drawing.naturalWidth
      let h = drawing.naturalHeight
    
      let size = w / 4 // Size (radius) of magnifying glass
      let magnification = 2
      let r = size / magnification // Radius of part we want to magnify
    
      let px = w / 3.5
      let py = h / 4
    
      let tileCanvas = document.getElementById("tCanvas")
      tileCanvas.width = 2 * size
      tileCanvas.height = 2 * size
    
      tileCanvas.getContext('2d').drawImage(canvas, px - r, py - r, 2 * r, 2 * r, 0, 0, 2 * size, 2 * size)
    
      let pattern = ctx.createPattern(tileCanvas, "repeat")
    
      ctx.fillStyle = pattern
    
      ctx.translate(px - size, py - size)
    
      ctx.beginPath()
      ctx.arc(size, size, size, 0, 2 * Math.PI)
      ctx.fill()
      ctx.strokeStyle = "orangered"
      ctx.lineWidth = 12
      ctx.stroke()
    }
    drawing.src = base64; // can also be a remote URL e.g. http://
    <canvas id="tCanvas" width=240 height=240 style="background-color:aqua;">
    </canvas>
    <canvas id="canvas1" width=240 height=240 style="background-color:#808080;">
    </canvas>
    <p></p>
    <button id="download" onclick="magnify();">Zoom</button>