Search code examples
javascripthtml5-canvas

How do I draw multiple images onto the canvas at once?


Right now the code is as follows:

const one = new Image(); 
one.src = "img/one.png";
const two = new Image();
two.src = "img/two.png";

function imgs(ctx) {
    one.onload = function () {
        ctx.drawImage(one, 50, 50);
    };
    two.onload = function () {
        ctx.drawImage(two, 100, 100);
    };
}

Can I wait for 'all' of the images to be loaded first, and than draw them all at the same time on the canvas? Currently it'll draw an image once it's been loaded, and than another once that is loaded, etc.


Solution

  • You can use create an image loader Promise, and then pass all three to Promise.all to await their load calls.

    The use of Promise and async/await is preferred over a separate counter variable.

    const ctx = document.querySelector('#draw').getContext('2d');
    
    const loadImage = (url) => new Promise((resolve, reject) => {
      const img = new Image();
      img.addEventListener('load', () => resolve(img));
      img.addEventListener('error', (err) => reject(err));
      img.src = url;
    });
    
    const imageUrls = [
      'https://via.placeholder.com/100/FF7777/FFFFFF?text=A',
      'https://via.placeholder.com/100/77FF77/FFFFFF?text=B',
      'https://via.placeholder.com/100/7777FF/FFFFFF?text=C'
    ];
    
    Promise
      .all(imageUrls.map(loadImage))
      .then(([one, two, three]) => {
        console.log('Drawing all three images...');
        ctx.drawImage(one, 0, 0);
        ctx.drawImage(two, 100, 0);
        ctx.drawImage(three, 200, 0);
      });
    <canvas id="draw" width="300" height="100"></canvas>


    Beginner help

    Selectors

    The document.querySelector method is preferred over the document.getElementById and document.getElementsByClassName because it it easier to work with and supports attribute selectors.

    This: document.querySelector('#draw') is preferred over document.getElementById('draw') or window.draw.

    Note: There is also a multi-element selector function called document.querySelectorAll

    Iterators

    The Array.prototype.map method returns a new array (iterates through the array) with a new value for each value visited. In the example above, each string is transformed into a Promise. The same code can be written more verbosely:

    Promise.all(imageUrls.map(function(imageUrl) {
      return loadImage(imageUrl); // returns a Promise<Image>
    }));