Il est assez courant de décoder des images pour les utiliser avec un canevas, que ce soit pour permettre aux utilisateurs de personnaliser un avatar, de recadrer une image ou simplement de faire un zoom avant sur une photo. Le problème du décodage des images est qu'il peut être gourmand en ressources processeur, ce qui peut parfois entraîner des à-coups ou un affichage en damier. Depuis Chrome 50 (et Firefox 42 ou version ultérieure), vous disposez d'une autre option: createImageBitmap()
. Il vous permet de décoder une image en arrière-plan et d'accéder à une nouvelle primitive ImageBitmap
, que vous pouvez dessiner dans un canevas de la même manière que vous le feriez pour un élément <img>
, un autre canevas ou une vidéo.
Dessiner des blobs avec createImageBitmap()
Imaginons que vous téléchargez une image blob avec fetch()
(ou XHR) et que vous souhaitez la dessiner dans un canevas. Sans createImageBitmap()
, vous devez créer un élément d'image et une URL de blob pour obtenir l'image dans un format que vous pouvez utiliser. Vous pouvez ainsi peindre beaucoup plus directement:
fetch(url)
.then(response => response.blob())
.then(blob => createImageBitmap(blob))
.then(imageBitmap => ctx.drawImage(imageBitmap, 0, 0));
Cette approche fonctionne également avec les images stockées en tant que blobs dans IndexedDB, ce qui fait des blobs un format intermédiaire pratique. En l'occurrence, Chrome 50 est également compatible avec la méthode .toBlob()
sur les éléments de canevas, ce qui signifie que vous pouvez, par exemple, générer des blobs à partir d'éléments de canevas.
Utiliser createImageBitmap() dans les nœuds de calcul Web
L'une des fonctionnalités les plus intéressantes de createImageBitmap()
est qu'elle est également disponible dans les nœuds de calcul. Vous pouvez donc désormais décoder des images où vous le souhaitez. Si vous avez de nombreuses images à décoder que vous considérez comme non essentielles, vous devez envoyer leurs URL à un nœud de travail Web, qui les téléchargera et les décodera au fur et à mesure. Il les transfère ensuite au thread principal pour les dessiner dans un canevas.
Le code permettant de procéder à cette opération peut se présenter comme suit:
// In the worker.
fetch(imageURL)
.then(response => response.blob())
.then(blob => createImageBitmap(blob))
.then(imageBitmap => {
// Transfer the imageBitmap back to main thread.
self.postMessage({ imageBitmap }, [imageBitmap]);
}, err => {
self.postMessage({ err });
});
// In the main thread.
worker.onmessage = (evt) => {
if (evt.data.err)
throw new Error(evt.data.err);
canvasContext.drawImage(evt.data.imageBitmap, 0, 0);
}
Aujourd'hui, si vous appelez createImageBitmap()
dans le thread principal, c'est là que le décodage sera effectué. Toutefois, l'objectif est que Chrome effectue automatiquement le décodage dans un autre thread, ce qui permet de réduire la charge de travail du thread principal. En attendant, vous devez toutefois faire attention à effectuer le décodage sur le thread principal, car il s'agit d'un travail intensif qui pourrait bloquer d'autres tâches essentielles, comme JavaScript, les calculs de style, la mise en page, la peinture ou le compositing.
Une bibliothèque d'aide
Pour me simplifier la vie, j'ai créé une bibliothèque d'assistance qui gère le décodage sur un worker, renvoie l'image décodée au thread principal et la dessine dans un canevas. Vous pouvez bien sûr procéder à une rétro-ingénierie et appliquer le modèle à vos propres applications. L'avantage principal est un contrôle accru, mais cela (comme d'habitude) s'accompagne de plus de code, de plus de débogage et de plus de cas particuliers à prendre en compte que l'utilisation d'un élément <img>
.
Si vous avez besoin de plus de contrôle sur le décodage des images, createImageBitmap()
est votre meilleur ami. Essayez-la dans Chrome 50 et dites-nous ce que vous en pensez !