首頁»前端»離屏Canvas — 使用Web Worker提高你的Canvas運行速度

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

來源:zcfy 發布時間:2018-12-01 閱讀次數:

  原文鏈接: developers.google.com

  現在因為有了離屏Canvas,你可以不用在你的主線程中繪制圖像了!

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

  它是非常靈活的,這意味著繪制在Canvas的內容可以被編程。舉個??,JavaScript就提供了Canvas的系列API。這些給了Canvas非常好的靈活度。

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

  幸運的是, OffscreenCanvas 離屏Canvas可以非常棒的解決這個麻煩!

  到目前為止,Canvas的繪制功能都與<canvas>標簽綁定在一起,這意味著Canvas API和DOM是耦合的。而OffscreenCanvas,正如它的名字一樣,通過將Canvas移出屏幕來解耦了DOM和Canvas API。

  由于這種解耦,OffscreenCanvas的渲染與DOM完全分離了開來,并且比普通Canvas速度提升了一些,而這只是因為兩者(Canvas和DOM)之間沒有同步。但更重要的是,將兩者分離后,Canvas將可以在Web Worker中使用,即使在Web Worker中沒有DOM。這給Canvas提供了更多的可能性。

 在Worker中使用OffscreenCanvas

  Workers 是一個Web版的線程——它允許你在幕后運行你的代碼。將你的一部分代碼放到Worker中可以給你的主線程更多的空閑時間,這可以提高你的用戶體驗度。就像其沒有DOM一樣,直到現在,在Worker中都沒有Canvas API。

  而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不多)的情況下,它都具有性能優勢。

 其它資源

QQ群:WEB開發者官方群(515171538),驗證消息:10000
微信群:加小編微信 849023636 邀請您加入,驗證消息:10000
提示:更多精彩內容關注微信公眾號:全棧開發者中心(fsder-com)
網友評論(共0條評論) 正在載入評論......
理智評論文明上網,拒絕惡意謾罵 發表評論 / 共0條評論
登錄會員中心
大发国际网址 临邑县| 师宗县| 萨迦县| 大名县| 突泉县| 淮阳县| 甘洛县| 雷州市| 建阳市| 太白县| 山阴县| 宜丰县| 上饶市| 东兰县| 沈丘县| 鸡东县| 察隅县| 鄂伦春自治旗| 朔州市| 宜都市| 德令哈市| 威信县| 西贡区| 甘泉县| 白河县| 海阳市| 马龙县| 临安市| 南投市| 泽普县| 新晃| 石屏县| 马边| 普洱| 马尔康县| 临沭县| 子长县| 林口县| 文安县| 耿马| 高雄市| 吴堡县| 蓬安县| 武陟县| 涿州市| 秦安县| 綦江县| 宣武区| 平顶山市| 保康县| 上犹县| 托克逊县| 绍兴市| 东乡| 玛沁县| 怀仁县| 佳木斯市| 万盛区| 闵行区| 墨竹工卡县| 普兰店市| 海伦市| 新营市| 射洪县| 博客| 梁平县| 盈江县| 旺苍县| 垫江县| 遂宁市| 漯河市| 南漳县| 永州市| 平山县| 抚松县| 余江县| 高淳县| 巴南区| 扶余县| 呼图壁县| 江达县| 南丰县| 大余县| 伊金霍洛旗| 镇江市| 涿鹿县| 深泽县| 高安市| 石棉县| 民和| 内丘县| 谷城县| 方山县| 巩义市| 万州区| 泗洪县| 迭部县| 繁昌县| 图木舒克市| 静海县| 原平市| 类乌齐县| 永康市| 汶上县| 鱼台县| 西和县| 西乌珠穆沁旗| 湖口县| 榆社县| 淳化县| 昭平县| 高淳县| 梅州市| 松桃| 芦山县| 乌什县| 济宁市| 长阳| 兴宁市| 红桥区| 鸡西市| 拜城县| 德兴市| 大连市| 新营市| 三门峡市| 伊宁市| 民丰县| 三河市| 宁化县| 五台县| 扶绥县| 岗巴县| 鱼台县| 济宁市| 道孚县| 巩留县|