Dekodowanie obrazów na potrzeby wykorzystania 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ć drżenie obrazu lub stosowanie techniki szachownicy. Od wersji Chrome 50 (oraz przeglądarki Firefox w wersji 42 i nowszych) dostępna jest kolejna opcja: 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 płótno 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óry możesz wykorzystać. 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. Jak to się dzieje, Chrome 50 obsługuje również metodę .toBlob()
w elementach canvas, co oznacza, że możesz na przykład generować obiekty blob z elementów canvas.
Używanie metody createImageBitmap() w procesach wątekowych w sieci
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 przeglądarki, która pobiera i dekoduje je w miarę dostępności. Następnie następuje przeniesienie ich z powrotem do wątku głównego w celu rysowania.
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 następnie 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. Największą korzyścią jest większa kontrola, ale (jak zwykle) potrzeba więcej kodu, więcej błędów do debugowania i większej liczby skrajnych przypadków, które można wziąć pod uwagę niż przy użyciu elementu <img>
.
Jeśli potrzebujesz większej kontroli nad dekodowaniem obrazu, createImageBitmap()
stanie się Twoim nowym najlepszym przyjacielem. Sprawdź tę funkcję w Chrome 50 i daj nam znać, jak Ci się podoba.