Das Entschlüsseln von Bildern für die Verwendung in einem Canvas ist ziemlich üblich, z. B. um Nutzern zu ermöglichen, einen Avatar anzupassen, ein Bild zuzuschneiden oder einfach nur heranzuzoomen. Das Problem bei der Dekodierung von Bildern ist, dass sie CPU-intensiv sein kann. Das kann manchmal zu Rucklern oder Schachbrettmustern führen. Ab Chrome 50 (und in Firefox 42 und höher) haben Sie jetzt eine weitere Option: createImageBitmap()
. Damit können Sie ein Bild im Hintergrund decodieren und auf ein neues ImageBitmap
-Element zugreifen, das Sie auf einem Canvas genauso zeichnen können wie ein <img>
-Element, ein anderes Canvas oder ein Video.
Blobs mit createImageBitmap() zeichnen
Angenommen, Sie laden ein Blob-Bild mit fetch()
(oder XHR) herunter und möchten es in ein Canvas zeichnen. Ohne createImageBitmap()
müssten Sie ein Bildelement und eine Blob-URL erstellen, um das Bild in ein verwendbares Format zu bringen. So können Sie viel direkter malen:
fetch(url)
.then(response => response.blob())
.then(blob => createImageBitmap(blob))
.then(imageBitmap => ctx.drawImage(imageBitmap, 0, 0));
Dieser Ansatz funktioniert auch mit Bildern, die als Blobs in IndexedDB gespeichert sind, was Blobs zu einem praktischen Zwischenformat macht. Chrome 50 unterstützt auch die .toBlob()
-Methode für Canvas-Elemente. Das bedeutet, dass Sie beispielsweise Blobs aus Canvas-Elementen generieren können.
createImageBitmap() in Webworkern verwenden
Eine der besten Funktionen von createImageBitmap()
ist, dass sie auch in Workern verfügbar ist. Sie können also jetzt Bilder überall decodieren. Wenn Sie viele Bilder decodieren müssen, die Sie für nicht wesentlich halten, senden Sie ihre URLs an einen Webworker, der sie nach und nach herunterlädt und decodiert. Diese werden dann an den Hauptthread zurückgegeben, um sie in ein Canvas zu zeichnen.

Der Code dazu könnte so aussehen:
// 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);
}
Wenn Sie createImageBitmap()
heute im Hauptthread aufrufen, erfolgt dort genau die Dekodierung. Künftig soll die Dekodierung jedoch automatisch in einem anderen Thread ausgeführt werden, um die Arbeitslast des Hauptthreads zu verringern. In der Zwischenzeit sollten Sie jedoch darauf achten, die Dekodierung im Haupt-Thread auszuführen, da es sich um eine intensive Arbeit handelt, die andere wichtige Aufgaben wie JavaScript, Stilberechnungen, Layout, Malen oder Compositing blockieren könnte.
Eine Hilfsbibliothek
Um das Ganze etwas einfacher zu machen, habe ich eine Hilfsbibliothek erstellt, die die Dekodierung auf einem Worker übernimmt, das decodierte Bild an den Hauptthread zurücksendet und es in ein Canvas zeichnet. Sie können es natürlich auch selbst entschlüsseln und das Modell auf Ihre eigenen Apps anwenden. Der Hauptvorteil besteht in mehr Kontrolle. Das bedeutet aber (wie üblich) mehr Code, mehr zu behebende Fehler und mehr Grenzfälle, die berücksichtigt werden müssen, als bei der Verwendung eines <img>
-Elements.
Wenn du mehr Kontrolle bei der Bilddekodierung benötigst, ist createImageBitmap()
genau das Richtige für dich. Probieren Sie es in Chrome 50 aus und lassen Sie uns wissen, wie es Ihnen gefällt.