Canvas 是一種在畫面上繪製各種圖形的常用方式,也是進入 WebGL 世界的入口。可用於繪製形狀、圖片、執行動畫,甚至顯示及處理影片內容。這類元件經常用於在媒體豐富的網頁應用程式和線上遊戲中,打造精美的使用者體驗。
它可透過指令碼操作,也就是說,您可以透過程式輔助方式建立在畫布上繪製的內容,例如在 JavaScript 中。這可讓畫布具備極佳的彈性。
同時,在現代網站中,指令碼執行是使用者回應速度問題最常見的來源之一。由於畫布邏輯和算繪作業會在與使用者互動相同的執行緒上執行,因此動畫中涉及的 (有時繁重) 運算可能會影響應用程式的實際和感知效能。
幸好,OffscreenCanvas 是對這項威脅的回應。
先前,畫布繪圖功能會與 <canvas>
元素綁定,也就是直接依附於 DOM。顧名思義,OffscreenCanvas 會將 DOM 和 Canvas API 分離,並將其移至畫面外。
由於這項分離作業,OffscreenCanvas 的轉譯作業會完全與 DOM 分離,因此相較於一般畫布,可提供一些速度上的改善,因為兩者之間沒有同步處理。
更重要的是,即使沒有可用的 DOM,也能在 Web Worker 中使用此方法。這可實現各種有趣的應用情境。
在 worker 中使用 OffscreenCanvas
Workers 是網頁版的執行緒,可讓您在背景執行工作。
將部分指令碼移至 worker 後,應用程式就能在主執行緒上執行對使用者而言至關重要的工作。沒有 OffscreenCanvas 的情況下,由於沒有可用的 DOM,因此無法在 worker 中使用 Canvas API。
OffscreenCanvas 不依賴 DOM,因此可以使用。以下範例使用 OffscreenCanvas 在 worker 中計算漸層顏色:
// 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 )
解除封鎖主執行緒
將大量運算工作移至 worker,即可釋出主執行緒上的大量資源。使用 transferControlToOffscreen 方法,將一般畫布鏡射至 OffscreenCanvas 例項。套用至 OffscreenCanvas 的作業會自動在來源畫布上算繪。
const offscreen = document.querySelector('canvas').transferControlToOffscreen();
const worker = new Worker('myworkerurl.js');
worker.postMessage({canvas: offscreen}, [offscreen]);
在以下範例中,當色彩主題變更時,系統會執行大量計算,即使是在速度快的電腦上,也需要幾毫秒的時間。您可以選擇在主執行緒或 worker 中執行動畫。在主執行緒的情況下,您無法在繁重工作執行時與按鈕互動,因為執行緒會遭到封鎖。在 worker 的情況下,UI 回應速度不會受到影響。
反之亦然:繁忙的主執行緒不會影響在 worker 上執行的動畫。您可以使用這項功能避免視覺卡頓,並確保即使有主執行緒流量,動畫也能流暢運作,如以下示範所示。
在一般畫布中,如果主執行緒人為過度超載,動畫就會停止,而以工作站為基礎的 OffscreenCanvas 則可順暢播放。
搭配常用程式庫使用
由於 OffscreenCanvas API 通常與一般 Canvas 元素相容,因此您可以將其用於漸進式增強功能,也可以搭配市面上一些主要的圖形程式庫使用。
舉例來說,您可以透過功能偵測功能,並在轉譯器建構函式中指定畫布選項,在可用時與 Three.js 搭配使用:
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});
這裡有個陷阱,就是 Three.js 預期畫布具有 style.width
和 style.height
屬性。OffscreenCanvas 完全與 DOM 分離,因此沒有這個值,您必須自行提供,方法是將其模擬出來,或提供將這些值與原始畫布尺寸連結的邏輯。
以下說明如何在 worker 中執行基本 Three.js 動畫:
請注意,某些 DOM 相關 API 無法在 worker 中使用,因此如果您想使用更進階的 Three.js 功能 (例如紋理),可能需要更多因應措施。如要瞭解如何開始嘗試這些功能,請觀看 2017 年 Google I/O 大會的影片。
如果您大量使用畫布的圖形功能,OffscreenCanvas 可以對應用程式效能產生正面影響。讓工作站可使用畫布轉譯內容,可提高網頁應用程式中的平行處理作業,並更有效地運用多核心系統。