0

I have an HTML5 canvas with the size of 392x392 in which I draw something. I am trying to resize the content of that canvas into another canvas with the size of 28x28 pixels.

The following approach works fine, except that I have to click the "resize image" button twice - why is that and how can I fix it?

function resize() {
  //get the base64 string of the Image
  var dataURL = canvas.toDataURL();
  //draw the base64 string into a 28x28 canvas (for resizing)
  var img = new Image();
  img.src = dataURL;
  resizedCtx.drawImage(img, 0, 0, 28, 28);
}
<canvas id="canvas" width="392" height="392" style="border:1px solid;"></canvas>
<br>
<br>
<input type="button" value="resize image" id="btn" size="30" onclick="resize()">
<br>
<br>
<canvas id="resizedCanvas" width="28" height="28" style="border:1px solid;"></canvas>

<script>
  canvas = document.getElementById('canvas');
  ctx = canvas.getContext("2d");

  resizedCanvas = document.getElementById('resizedCanvas');
  resizedCtx = resizedCanvas.getContext('2d');
    
  ctx.fillStyle = "red";
  ctx.fillRect(20, 20, 280, 280);
</script>

code in jsfiddle.net

Thanks in advance!

Tom
  • 151
  • 1
  • 1
  • 8
  • **Important note:** do not even go through an img to resize your canvas. Simply draw the canvas directly over the resized one. – Kaiido Nov 24 '18 at 23:26

4 Answers4

2

Your Image object not yet finished loading its content at the time you try to drawImage. You must wait until the image fires a load event. Before that event fires, the image will not render as anything, because it has not yet finished loading data from its src URL.

Your code works after a second click because the browser has cached the image data associated with that particular src after the first click, so it is ready to render immediately on the second click.

Simply place your resizedCtx.drawImage(...) call inside a callback function that will not run until chronologically after a load event fires for img.

function resize() {
  //get the base64 string of the Image
  var dataURL = canvas.toDataURL();
  //draw the base64 string into a 28x28 canvas (for resizing)
  var img = new Image();
  img.src = dataURL;
  img.addEventListener("load", function() {
      resizedCtx.drawImage(img, 0, 0, 28, 28);
  });
}

function resize() {
//get the base64 string of the Image
var dataURL = canvas.toDataURL();
//draw the base64 string into a 28x28 canvas (for resizing)
var img = new Image();
  img.src = dataURL;
  img.addEventListener("load", function() {
    resizedCtx.drawImage(img, 0, 0, 28, 28);
  });
}
<canvas id="canvas" width="392" height="392" style="border:1px solid;"></canvas>
<br><br>
<input type="button" value="resize image" id="btn" size="30" onclick="resize()">
<br><br>
<canvas id="resizedCanvas" width="28" height="28" style="border:1px solid;"></canvas>
<script>
  canvas = document.getElementById('canvas');
  ctx = canvas.getContext("2d");
  resizedCanvas = document.getElementById('resizedCanvas');
  resizedCtx = resizedCanvas.getContext('2d');
  ctx.fillStyle = "red";
  ctx.fillRect(20, 20, 280, 280);

</script>
NewToJS
  • 2,762
  • 3
  • 14
  • 22
apsillers
  • 112,806
  • 17
  • 235
  • 239
  • Great answer! **+1** I hope you don't mind but I have added a Snippet to show your changes work as expected. I have it set to hide by default so it doesn't take up too much space and make your post look messy. – NewToJS Nov 24 '18 at 22:39
  • Thank you very much! This works fine! – Tom Nov 24 '18 at 22:45
2

You can replace your JS code with this

function resize() {
    var myCanvas = document.getElementById('resizedCanvas');
    var ctx = myCanvas.getContext('2d');
    var img = document.getElementById('canvas');
    ctx.drawImage(img,0,0);
}

Canvas will accept another canvas content. All you can do now is manipulate variable image to change it's positions and there you have it.

0

function resize() {
  //get the base64 string of the Image
  var dataURL = canvas.toDataURL();
  //draw the base64 string into a 28x28 canvas (for resizing)
  var img = new Image();
  img.onload = function() {
    resizedCtx.drawImage(img, 0, 0, 28, 28);
  };
  img.src = dataURL;
}
<canvas id="canvas" width="392" height="392" style="border:1px solid;"></canvas>
<br>
<br>
<input type="button" value="resize image" id="btn" size="30" onclick="resize()">
<br>
<br>
<canvas id="resizedCanvas" width="28" height="28" style="border:1px solid;"></canvas>

<script>
  canvas = document.getElementById('canvas');
  ctx = canvas.getContext("2d");
  resizedCanvas = document.getElementById('resizedCanvas');
  resizedCtx = resizedCanvas.getContext('2d');
  ctx.fillStyle = "red";
  ctx.fillRect(20, 20, 280, 280);
</script>

Just put the resizedCanvas and resizedCtx line inside the resize function.

Abhishek
  • 382
  • 1
  • 6
0

you can use img.onload in other to load the image and then execute the funtion afterwards function resize() { //get the base64 string of the Image var dataURL = canvas.toDataURL(); //draw the base64 string into a 28x28 canvas (for resizing) var img = new Image(); img.src = dataURL; img.onload = function () { resizedCtx.drawImage(img, 0, 0, 28, 28); } }

just replace the input tag with the one here it will fix the problem.

function resize() {
  //get the base64 string of the Image
  var dataURL = canvas.toDataURL();
  //draw the base64 string into a 28x28 canvas (for resizing)
  var img = new Image();
  img.src = dataURL;
  img.onload = function () { 
  resizedCtx.drawImage(img, 0, 0, 28, 28);
    }
}
<canvas id="canvas" width="392" height="392" style="border:1px solid;"></canvas>
<br>
<br>
<input type="button" value="resize image" id="btn" size="30" onclick="resize()">
<br>
<br>
<canvas id="resizedCanvas" width="28" height="28" style="border:1px solid;"></canvas>

<script>
 canvas = document.getElementById('canvas');
  ctx = canvas.getContext("2d");

  resizedCanvas = document.getElementById('resizedCanvas');
  resizedCtx = resizedCanvas.getContext('2d');
    
  ctx.fillStyle = "red";
  ctx.fillRect(20, 20, 280, 280);
</script>
soben360
  • 31
  • 1
  • 5