Dekodowanie obrazów na potrzeby użycia na płótnie jest dość powszechne, niezależnie od tego, czy chodzi o umożliwienie użytkownikom dostosowania awatara, przycięcie obrazu czy po prostu powiększenie zdjęcia. Problem z dekodowaniem obrazów polega na tym, że może ono wymagać dużej mocy procesora, co może czasami powodować trzęsienie lub szachownicy. Od wersji 50 Chrome (i Firefoksa 42+) masz do dyspozycji kolejną opcję: createImageBitmap()
. Umożliwia dekodowanie obrazu w tle i uzyskanie dostępu do nowego prymitywu ImageBitmap
, który możesz narysować na płótnie w taki sam sposób jak element <img>
, inne tło lub film.
Rysowanie plamek za pomocą metody createImageBitmap()
Załóżmy, że pobierasz obraz bloba za pomocą fetch()
(lub XHR) i chcesz narysować go na płótnie. Bez createImageBitmap()
musisz utworzyć element obrazu i adres URL Blob, aby uzyskać obraz w formacie, którego możesz użyć. Dzięki temu możesz szybciej dotrzeć do obrazu:
fetch(url)
.then(response => response.blob())
.then(blob => createImageBitmap(blob))
.then(imageBitmap => ctx.drawImage(imageBitmap, 0, 0));
To podejście będzie też działać w przypadku obrazów przechowywanych jako bloby w IndexedDB, dzięki czemu bloby staną się wygodnym formatem pośrednim. Chrome 50 obsługuje też metodę .toBlob()
w przypadku elementów kanwy, co oznacza, że możesz na przykład generować plamki z elementów kanwy.
Używanie metody createImageBitmap() w procesach wątekowych
Jedną z najlepszych funkcji usługi createImageBitmap()
jest to, że jest ona też dostępna w instancjach roboczych, co oznacza, że możesz teraz dekodować obrazy, gdziekolwiek chcesz. Jeśli masz dużo obrazów do dekodowania, które uważasz za nieistotne, możesz wysłać ich adresy URL do instancji roboczej w sieci, która pobiera i dekoduje je w miarę dostępności. Następnie zostaną one przeniesione z powrotem do wątku głównego, aby można było rysować na płótnie.
Kod, który to umożliwia, może wyglądać tak:
// 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);
}
Jeśli wywołasz funkcję createImageBitmap()
w wątku głównym, dekodowanie zostanie wykonane właśnie w tym miejscu. Planujemy jednak, aby Chrome automatycznie dekodował w innym wątku, co pozwoli ograniczyć obciążenie wątku głównego. Pamiętaj jednak, aby dekodowanie odbywało się w wątku głównym, ponieważ jest to intensywne działanie, które może blokować inne ważne zadania, takie jak JavaScript, obliczenia stylu, układ, malowanie czy kompozytowanie.
Biblioteka pomocnicza
Aby ułatwić sobie życie, utworzyłem bibliotekę pomocniczą, która zajmuje się dekodowaniem na wątku pomocniczym, a potem wysyła z powrotem odkodowany obraz do wątku głównego i nanosi go na kanwę. Oczywiście możesz przeanalizować model wstecznie i zastosować go w swoich aplikacjach. Główną zaletą jest większa kontrola, ale (jak zwykle) wiąże się to z większą ilością kodu, więcej błędów do debugowania i więcej skrajnych przypadków do rozważenia niż w przypadku elementu <img>
.
Jeśli potrzebujesz większej kontroli nad dekodowaniem obrazu, createImageBitmap()
stanie się Twoim nowym najlepszym przyjacielem. Sprawdź ją w Chrome 50 i daj nam znać, jak Ci się podoba.