Canvas to popularny sposób rysowania na ekranie różnego rodzaju grafiki. Jest też punktem wejścia do świata WebGL. Możesz go używać do rysowania kształtów, obrazów, uruchamiania animacji, a nawet wyświetlania i przetwarzania treści wideo. Jest on często używany do tworzenia atrakcyjnych aplikacji internetowych i gier online zawierających wiele multimediów.
Można go skonfigurować za pomocą skryptu, co oznacza, że treści narysowane na płótnie mogą być tworzone programowo, na przykład w JavaScript. Daje to dużą elastyczność.
Jednocześnie w przypadku nowoczesnych witryn wykonanie skryptu jest jednym z najczęstszych źródeł problemów z szybkością wczytywania. Logika i renderowanie płótna odbywają się w tym samym wątku co interakcje z użytkownikiem, dlatego (czasami obciążające) obliczenia związane z animowaniem mogą negatywnie wpływać na rzeczywistą i postrzeganą wydajność aplikacji.
Na szczęście OffscreenCanvas jest odpowiedzią na to zagrożenie.
Wcześniej funkcje rysowania na płótnie były powiązane z elementem <canvas>
, co oznaczało, że zależały bezpośrednio od DOM. Jak sama nazwa wskazuje, element OffscreenCanvas odłącza DOM i interfejs Canvas API, przenosząc go poza ekran.
Dzięki temu oddzieleniu renderowanie OffscreenCanvas jest całkowicie niezależne od DOM i w związku z tym oferuje pewne ulepszenia szybkości w porównaniu ze zwykłym canvasem, ponieważ nie ma synchronizacji między nimi.
Co więcej, można go używać w Web Workerze, nawet jeśli nie ma dostępnego DOM. Umożliwia to różne ciekawe zastosowania.
Używanie OffscreenCanvas w instancjach roboczych
Workers to odpowiednik wątków w internecie. Umożliwiają one wykonywanie zadań w tle.
Przeniesienie części skryptu do wątku roboczego daje aplikacji więcej miejsca na wykonywanie zadań ważnych dla użytkownika na wątku głównym. Bez interfejsu OffscreenCanvas nie można było używać interfejsu Canvas API w workerze, ponieważ nie było dostępnej pamięci DOM.
Element OffscreenCanvas nie zależy od DOM, więc można go używać. W tym przykładzie do obliczenia koloru gradientu w elementach worker użyto metody OffscreenCanvas:
// file: worker.js
function getGradientColor(percent) {
const canvas = new OffscreenCanvas(100, 1);
const ctx = canvas.getContext('2d');
const gradient = ctx.createLinearGradient(0, 0, canvas.width, 0);
gradient.addColorStop(0, 'red');
gradient.addColorStop(1, 'blue');
ctx.fillStyle = gradient;
ctx.fillRect(0, 0, ctx.canvas.width, 1);
const imgd = ctx.getImageData(0, 0, ctx.canvas.width, 1);
const colors = imgd.data.slice(percent * 4, percent * 4 + 4);
return `rgba(${colors[0]}, ${colors[1]}, ${colors[2]}, ${colors[3]})`;
}
getGradientColor(40); // rgba(152, 0, 104, 255 )
Odblokowanie wątku głównego
Przeniesienie intensywnych obliczeń do instancji roboczej pozwala zwolnić znaczne zasoby w głównym wątku. Aby odzwierciedlić zwykłe tworzywo w przypadku OffscreenCanvas, użyj metody transferControlToOffscreen. Operacje zastosowane do OffscreenCanvas zostaną automatycznie wyrenderowane na płótnie źródłowym.
const offscreen = document.querySelector('canvas').transferControlToOffscreen();
const worker = new Worker('myworkerurl.js');
worker.postMessage({canvas: offscreen}, [offscreen]);
W tym przykładzie intensywne obliczenia występują podczas zmiany motywu kolorów. Powinny one zająć kilka milisekund nawet na szybkim komputerze stacjonarnym. Możesz uruchomić animacje na wątku głównym lub w procesie. W przypadku wątku głównego nie możesz wchodzić w interakcje z przyciskiem, gdy wykonywane jest wymagające dużo zasobów zadanie – wątek jest zablokowany. W przypadku pracownika nie ma wpływu na szybkość reakcji interfejsu.
Działa to też w drugą stronę: zajęty wątek główny nie wpływa na animację działającą na wątku roboczym. Dzięki tej funkcji możesz uniknąć zacięcia się obrazu i zapewnić płynną animację pomimo ruchu w głównym wątku, jak pokazano w tym filmie demonstracyjnym.
W przypadku zwykłego kanwy animacja zatrzymuje się, gdy wątki główne są sztucznie przeciążone, a tego samego typu OffscreenCanvas na podstawie wątku pomocniczego odtwarza się płynnie.
Korzystanie z popularnych bibliotek
Interfejs OffscreenCanvas API jest ogólnie zgodny ze standardowym elementem Canvas, więc możesz go używać jako stopniowego ulepszenia, także w przypadku niektórych wiodących bibliotek graficznych na rynku.
Możesz na przykład wykryć tę funkcję i jeśli jest dostępna, użyć jej w Three.js, podając opcję canvas w konstrukcji renderowania:
const canvasEl = document.querySelector('canvas');
const canvas =
'OffscreenCanvas' in window
? canvasEl.transferControlToOffscreen()
: canvasEl;
canvas.style = {width: 0, height: 0};
const renderer = new THREE.WebGLRenderer({canvas: canvas});
Tutaj pojawia się jeden problem: Three.js oczekuje, że canvas ma mieć właściwości style.width
i style.height
.
OffscreenCanvas, jako całkowicie odłączony od DOM, nie ma tych informacji, więc musisz je podać samodzielnie, albo przez ich zastąpienie, albo przez podanie logiki, która łączy te wartości z oryginalnymi wymiarami kanwy.
Poniżej znajdziesz instrukcje uruchamiania podstawowej animacji Three.js w procesie roboczym:
Pamiętaj, że niektóre interfejsy API związane z DOM nie są łatwo dostępne w procesie workera, więc jeśli chcesz korzystać z bardziej zaawansowanych funkcji Three.js, takich jak tekstury, może być konieczne zastosowanie innych rozwiązań. Aby dowiedzieć się, jak zacząć eksperymentować z tymi funkcjami, obejrzyj film z Google I/O 2017.
Jeśli często korzystasz z możliwości graficznych canvasa, funkcja OffscreenCanvas może pozytywnie wpłynąć na wydajność aplikacji. Udostępnianie kontekstów renderowania na potrzeby procesu uruchamiania zwiększa równoległość w aplikacjach internetowych i poprawia wykorzystanie systemów wielordzeniowych.