離屏Canvas — 使用Web Worker提升你的Canvas運行速度

離屏Canvas — 使用Web Worker提升你的Canvas運行速度

原文連接:  developers.google.com

如今由於有了離屏Canvas,你能夠不用在你的主線程中繪製圖像了!html

Canvas 是一個很是受歡迎的表現方式,同時也是WebGL的入口。它能繪製圖形,圖片,展現動畫,甚至是處理視頻內容。它常常被用來在富媒體web應用中建立炫酷的用戶界面或者是製做在線(web)遊戲。git

它是很是靈活的,這意味着繪製在Canvas的內容能夠被編程。舉個🌰,JavaScript就提供了Canvas的系列API。這些給了Canvas很是好的靈活度。github

但同時,在一些現代化的web站點,腳本解析運行是實現流暢用戶反饋的最大的問題之一。由於Canvas計算和渲染和用戶操做響應都發生在同一個線程中,在動畫中(有時候很耗時)的計算操做將會致使App卡頓,下降用戶體驗。web

幸運的是, OffscreenCanvas 離屏Canvas能夠很是棒的解決這個麻煩!chrome

到目前爲止,Canvas的繪製功能都與<canvas>標籤綁定在一塊兒,這意味着Canvas API和DOM是耦合的。而OffscreenCanvas,正如它的名字同樣,經過將Canvas移出屏幕來解耦了DOM和Canvas API。編程

因爲這種解耦,OffscreenCanvas的渲染與DOM徹底分離了開來,而且比普通Canvas速度提高了一些,而這只是由於二者(Canvas和DOM)之間沒有同步。但更重要的是,將二者分離後,Canvas將能夠在Web Worker中使用,即便在Web Worker中沒有DOM。這給Canvas提供了更多的可能性。canvas

在Worker中使用OffscreenCanvas

Workers 是一個Web版的線程——它容許你在幕後運行你的代碼。將你的一部分代碼放到Worker中能夠給你的主線程更多的空閒時間,這能夠提升你的用戶體驗度。就像其沒有DOM同樣,直到如今,在Worker中都沒有Canvas API。markdown

而OffscreenCanvas並不依賴DOM,因此在Worker中Canvas API能夠被某種方法來代替。下面是我在Worker中用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[]); } getGradientColor(40); // rgba(152, 0, 104, 255 ) 

不要阻塞主線程

當咱們將大量的計算移到Worker中運行時,能夠釋放主線程上的資源,這頗有意思。咱們可使用transferControlToOffscreen 方法將常規的Canvas映射到OffscreenCanvas實例上。以後全部應用於OffscreenCanvas的操做將自動呈如今在源Canvas上。函數

const offscreen = document.querySelector('canvas').transferControlToOffscreen(); const worker = new Worker('myworkerurl.js'); worker.postMessage({ canvas: offscreen }, [offscreen]); 

OffscreenCanvas 是 [可轉移的](https://developer.mozilla.org/en-US/docs/Web/API/Transferable))。除了將其指定爲傳遞信息中的字段之一之外,還須要將其做爲postMessage(傳遞信息給Worker的方法)中的第二個參數傳遞出去,以即可以在Worker線程的context(上下文)中使用它。

在下面的🌰中,當顏色主題發生變化時會發生「複雜的計算」,這個計算即便在高性能的臺式機上也要花費幾毫秒。而你能夠選擇在主線程或Worker上運行這段動畫。在主線程下,當複雜計算開始運行時,你將沒法與按鈕交互 - 線程被阻塞掉了。而在Worker下,UI的響應並無被影響。

Demo

它也是另外一種解釋方式:任務繁忙的主線程也不會影響在Worker上運行的動畫。因此即便主線程很是繁忙,你也能夠經過此功能來避免掉幀並保證流暢的動畫:

Demo

上例展現了在普通Canvas的下,當主線程被添加繁忙任務時動畫被阻塞了,而基於Worker的OffscreenCanvas播放卻很流利。

與流行庫一塊兒使用

得益於OffscreenCanvas API通常狀況下與常規Canvas元素的相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須要Canvas具備style.width和style.height屬性。而OffscreenCanvas是與DOM徹底分離的,沒有這些屬性。因此你須要本身提供這些屬性,或者經過將其從three.js邏輯中刪除或者自行編寫這些值與初始Canvas尺寸相關聯的邏輯。

下面是一個運行基本Three.js動畫的demo:

Demo

可是請記住,有一些與DOM相關的API在Worker中並不容易得到,所以若是你想使用更高級的Three.js功能(好比紋理)的話,可能須要更多變通的方法。有關這方面已經開始嘗試的一些想法,請查看 Google I/O 2017的視頻

此視頻的示例中出現的commit()方法咱們並不推薦。請改用worker.requestAnimationFrame。

結論

若是你對圖像繪畫使用得很是多,OffscreenCanvas能夠有效的提升你APP的性能。它使得Worker能夠處理canvas的渲染繪製,讓你的APP更好地利用了多核系統。

OffscreenCanvas在Chrome 69中已經不須要開啓flag(實驗性功能)就可使用了。它也正在被 Firefox 實現。因爲其API與普通canvas元素很是類似,因此你能夠輕鬆地對其進行特徵檢測並按部就班地使用它,而不會破壞現有的APP或庫的運行邏輯。OffscreenCanvas在任何涉及到圖形計算以及動畫表現且與DOM關係並不密切(即依賴DOM API很少)的狀況下,它都具備性能優點。

其它資源

相關文章
相關標籤/搜索