Loader d’images en javascript

Vous avez du probablement vous rendre compte que lorsque vous affichiez une image dans un canvas html5, celle-ci n’apparaissait pas immédiatement et parfois nécessitait un rafraichissement de la page. La faute à quoi ? Tout simplement au délai inhérent chargement de l’image en mémoire : l’image était affichée avant qu’elle ne soit chargée en mémoire, elle était donc invisible au moment où elle était affichée.

Pour éviter ce genre de problème, il suffit d’attendre que l’image soit effectivement chargée. Je vous explique et vous montre comment faire dans cet article. Vous allez aussi en apprendre sur les promesses javascript (promise)

Attendre que l’image soit chargée

Il existe plusieurs manières de le faire, l’un d’entre elles étant l’usage des promesses (javascript Promise).

 

Création de la librairie javascript : 1ère méthode

La méthode consiste à créer un objet auquel on donnera une liste d’images en en spécifiant le chemin (add) et d’invoquer une méthode de chargement load.

La méthode de chargement crée alors des objets javascript image pour chacun des chemins ajoutés.

Chaque fois qu’une image est chargée; un compteur d’images chargées est incrémenté.

Lorsque le compteur d’images chargées atteint le nombre total d’images ajoutées, un événement imageLoaded est diffusé pour l’indiquer.
Si un chargement est en erreur, un événement imageError est diffusé pour l’indiquer.

Evénement qu’il suffit d’intercepter pour être averti du chargement complet.

let ImageLoader = {
 
 imgPathList : Array(),
 imgList : Array(),
 imgCountLoaded : 0,
   
 add : function(img) {
  this.imgPathList.push(img);
 },
   
 load : function() {
  let loader = this;
  loader.imgPathList.forEach(function(imgPath) {
   let img = new Image();
   img.src = imgPath;
   loader.imgList.push(img);
       
   img.addEventListener("load", function() {
    loader.imgCountLoaded++;
    if ( loader.imgCountLoaded == loader.imgPathList.length ) {
     let doneEvent = new Event('imageLoaded');
     document.dispatchEvent(doneEvent);
    }
   });
 
   img.addEventListener("error", function(e) {
    let errorEvent = new CustomEvent('imageError', { 'detail' : 'Unable to load ' + e.srcElement.currentSrc } );
    loader.imgCountLoaded++;      
    document.dispatchEvent(errorEvent);
   });
 
  });
 }
};

On utilise l’objet de la manière suivante.

ImageLoader.add("1.png");
ImageLoader.add("2.png");
ImageLoader.add("3.png");
ImageLoader.add("4.png");
ImageLoader.add("5.png");
 
document.addEventListener("imageLoaded", function(e) {
 // Le chargement est terminé
});
 
document.addEventListener("imageError", function(e) {
 // Le chargement est en erreur
 console.log("msgError: " + e.detail);
});
   
ImageLoader.load();

Création de la librairie javascript : 2ème méthode avec Promise

La première étape est de créer une promesse qui est :
– résolue lorsque l’image donnée par le biais de son chemin en paramètre est chargée;
– en échec dans le cas contraire.

Lorsqu’une image est chargée, un événement load est déclenché. C’est cet événement qui donnera l’indication de résolution et donc déclenchera le traitement approprié via resolve.
En cas d’erreur, un événement error est déclenché. C’est cet événement qui donnera l’indication d’échec et donc déclenchera le traitement approprié via reject.

 

Le code de la promesse

function loadImagePromise(url) {
  return new Promise((resolve, reject) => {
    let image = new Image();
    image.onload = () => resolve(image);
    const msg = 'Unable to load image at '+ url;
    image.onerror = () => reject(new Error(msg));
    image.src = url;  
  })
}

En notation non raccourcie.

function loadImagePromise(url) {
  return new Promise((resolve, reject) => {
    let image = new Image();
    image.onload = function() {resolve(image)};
    const msg = 'Unable to load image at '+ url;
    image.onerror = function() {reject(new Error(msg))};
    image.src = url;  
  })
}

Vous chargez une image avec :

loadImagePromise('chemin_vers_image');

En général, du moins dans la production d’un jeu vidéo html5, vous ne chargez pas qu’une seule image mais toute une série.

let p1 = loadImagePromise('1.png');
let p2 = loadImagePromise('2.png');
let p3 = loadImagePromise('3.png');
let p4 = loadImagePromise('4.png');

Il faut donc traiter les promesses les unes après les autres.

let p1 = loadImagePromise('1.png');
p1.then(function() {alert("loaded");})
  .catch(function(error) {alert("not loaded " + error);});
let p2 = loadImagePromise('2.png');
p2.then(function() {alert("loaded");})
  .catch(function(error) {alert("not loaded " + error);});
let p3 = loadImagePromise('3.png');
p3.then(function() {alert("loaded");})
  .catch(function(error) {alert("not loaded " + error);});
let p4 = loadImagePromise('4.png');
p4.then(function() {alert("loaded");})
  .catch(function(error) {alert("not loaded " + error);});

Un peu lourd comme procédé. Pour faire plus court, javascript prévoit de traiter les promesses en lot par le biais d’un tableau de promesses passé en paramètre de la méthode all comme ceci.

Promise.all(imageList)
  .then(function() {alert("all are loaded");})
  .catch(function(error) {alert("not loaded " + error);});

Le tableau imageList est lui constitué des promesses javascript.

const imageList = [
  loadImagePromise('1.png'),
  loadImagePromise('2.png'),
  loadImagePromise('3.png')
  loadImagePromise('4.png')
 ];

L’usage des javascript promise est plus moderne mais n’est pas incontournable.

Apprenez à créer un jeu vidéo en une soirée.

Accessible à tous. Pour moins de 10€.
Dernières places disponibles pour l'année 2024.

Ce format court sera abandonné en 2025.

Voir l'événement

Bravo, jette un œil à ta boite mail pour télécharger ton guide.