'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 1:[1]

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>

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 obscure