javascript canvas doesn’t draw images

Tags: , , , ,



I’m developing hybrid javascript app for android using cordova.
In the following code I use two ways of drawing image on canvas: with setTimeout and without.
Following code (wrapped with cordova) on android device doesn’t react on func1 but does react on func2. The second click on func1 finally draws image on a canvas. Which is completely strange.

I assume it has something to do with android device performance because on my desktop PC both functions works fine.

Why this happening? How to avoid using setTimeout?

<html style="background: white;">
  <head>
  </head>
  <body>
    <button onclick="func1()">render img2 func1</button>
    <button onclick="func2()">render img2 func2</button><br />
    <canvas id="canv">canv</canvas>
    
    <script>
      var img = new Image();
      var canvas = document.getElementById('canv');
      canvas.width = 100;
      canvas.height = 100;
      var ctx = canvas.getContext("2d");
        
      function setSrc() {
        img.src = "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAADIAAAAyCAYAAAAeP4ixAAAASUlEQVRo3u3PAQ0AIAwDsIGC+TcLLkhOWgddSU6Ga5udT4iIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIiIi8cQEjUgGTmE6z3QAAAABJRU5ErkJggg=="
      };
        
      function drawImg() {
        ctx.drawImage(img, 0, 0, canvas.width, canvas.height);
      };
    
      function func1() {
        setSrc();
        drawImg();
      };
        
      function func2() {
        setSrc();
        setTimeout(function () {
          drawImg();
        }, 500);
      };
     
    </script>
  </body>
</html>

Answer

It’s not so strange as image loading is asynchronous. You are trying to draw the image before it has loaded. On the second click the image has loaded so therefor it will be drawn.

You need to use a callback mechanism in order to make this work:

function setSrc(callback) {
    img.onload = callback;   /// when image is loaded this will be called
    img.src = 'data: ...snipped...';
};

function drawImg() {
    ctx.drawImage(img, 0, 0, canvas.width, canvas.height);
};

Then modify your function slightly:

function func1() {
    setSrc(drawImg);  /// drawImg is now callback function for setSrc.
};

Note: if you need to for example draw things on top of the image you must continue your code from the callback function. For example add a callback also for the drawImg function which calls the next step in the code after the image has been drawn. This is because as mentioned, image loading is asynchronous and if you attempt to draw anything else before the image has been loaded the image will be drawn on top instead



Source: stackoverflow