Chrome supporta createImageBitmap() in Chrome 50

Paul Lewis

La decodifica delle immagini da utilizzare con una tela è piuttosto comune, ad esempio per consentire agli utenti di personalizzare un avatar, ritagliare un'immagine o semplicemente aumentare lo zoom su un'immagine. Il problema della decodifica delle immagini è che può utilizzare molta CPU e questo a volte può significare jank o checkerboarding. A partire da Chrome 50 (e da Firefox 42 e versioni successive) ora è disponibile un'altra opzione: createImageBitmap(). Ti consente di decodificare un'immagine sullo sfondo e di ottenere l'accesso a una nuova primitiva di ImageBitmap, che puoi disegnare in una tela esattamente come faresti con un elemento <img>, un'altra tela o un video.

Disegno di BLOB con createImageBitmap()

Supponiamo che tu scarichi un'immagine blob con fetch() (o XHR) e vuoi disegnarla in una tela. Senza createImageBitmap(), dovresti creare un elemento immagine e un URL BLOB per convertire l'immagine in un formato utilizzabile. Offre un percorso molto più diretto verso la pittura:

fetch(url)
    .then(response => response.blob())
    .then(blob => createImageBitmap(blob))
    .then(imageBitmap => ctx.drawImage(imageBitmap, 0, 0));

Questo approccio funziona anche con le immagini archiviate come BLOB in IndexedDB, che rappresentano un comodo formato intermedio. Chrome 50 supporta anche il metodo .toBlob() per gli elementi canvas, il che significa che puoi, ad esempio, generare BLOB dagli elementi canvas.

Utilizzo di createImageBitmap() nei web worker

Una delle caratteristiche più interessanti di createImageBitmap() è che è disponibile anche nei worker, il che significa che ora puoi decodificare le immagini ovunque vuoi. Se hai molte immagini da decodificare che consideri non essenziali, spedi i loro URL a un web worker, che le scaricherà e le decodificherà quando il tempo lo permetterà. Quindi, li ha trasferiti di nuovo al thread principale per il disegno su una tela.

Flusso di dati con createImageBitmap e web worker.

Il codice da utilizzare potrebbe essere simile al seguente:

// 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);
}

Oggi, se chiami createImageBitmap() nel thread principale, la decodifica verrà effettuata esattamente su questo punto. Tuttavia, è in programma che Chrome esegua automaticamente la decodifica in un altro thread, per ridurre il carico di lavoro del thread principale. Nel frattempo, tuttavia, dovresti essere consapevole di eseguire la decodifica sul thread principale, poiché si tratta di un lavoro intensivo che potrebbe bloccare altre attività essenziali, come JavaScript, calcoli di stile, layout, disegno o composizione.

Una libreria di supporto

Per semplificare un po' la vita, ho creato una libreria di supporto che gestisce la decodifica su un worker, invia l'immagine decodificata al thread principale e la disegna in un canvas. Dovresti, ovviamente, eseguire il reverse engineering e applicare il modello alle tue app. Il vantaggio principale è un maggiore controllo, che però (come di consueto) comporta più codice, più operazioni di debug e più casi limite da prendere in considerazione rispetto all'uso di un elemento <img>.

Se hai bisogno di un maggiore controllo con la decodifica delle immagini, createImageBitmap() è il tuo nuovo migliore amico. Provala in Chrome 50 e facci sapere come te la cavi.