<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          從 fabric 源碼來看如何做好一個 canvas 庫

          共 3777字,需瀏覽 8分鐘

           ·

          2021-10-15 17:04

          關(guān)注并將「趣談前端」設(shè)為星標(biāo)

          每天定時分享技術(shù)干貨/優(yōu)秀開源/技術(shù)思維

          前言,本篇文章可以讓你從 0 到 1 快速了解 canvas,了解 fabricjs 源碼庫的整體組織和關(guān)鍵核心代碼。

          • canvas 是什么,它的渲染原理
          • retina 屏模糊的原理以及解決方法
          • fabricjs 核心關(guān)鍵優(yōu)化:事件驅(qū)動、retina 屏處理、渲染優(yōu)化(分層,requestAnimationFrame)、繪制對象的各種繼承關(guān)系。

          Canvas 是什么?

          Canvas 是什么東西?它跟傳統(tǒng)的 DOM 有什么區(qū)別?我們先看看官方的定義:

          ?

          「Canvas API」 提供了一個通過 JavaScriptHTML 的 canvas 元素來繪制圖形的方式。它可以用于動畫、游戲畫面、數(shù)據(jù)可視化、圖片編輯以及實(shí)時視頻處理等方面。

          ?

          簡單的,我們可以理解為:Canvas 是一個畫布,然后我們可以使用 JS 做為畫筆在上面繪制東西。

          Canvas 渲染原理

          眾所周知,傳統(tǒng)的 DOM 動畫在主線程渲染的流程如下:

          • DOM 動畫:

          • Canvas 渲染流程:

          我們知道在瀏覽器上渲染動畫,每一秒最高達(dá)到 60 幀左右,換句話說,如果我們能在 16.7 毫秒內(nèi)完成圖像的計算與渲染過程,那視覺呈現(xiàn)就可以達(dá)到完美的 60fps。這樣界面就不會卡頓。

          從上面的圖對比看出,Canvas 能夠直接操作繪圖上下文,不需要經(jīng)過 HTML、CSS 解析、構(gòu)建渲染樹、布局等一系列操作。因此單純繪圖的話,Canvas 比 HTML/CSS 和 SVG 要快得多,同樣移動大量的元素,Canvas 的性能也會比傳統(tǒng) DOM 好一些。

          Canvas 是一個即時繪制的模式,這意味著繪圖表面不會保留所繪制對象的信息。傳統(tǒng)的 DOM 模式需要維護(hù)對應(yīng)的 DOM 層次結(jié)構(gòu)。需要額外的內(nèi)存來保存場景,并且更新上也會更慢一點(diǎn)。所以,Canvas 占用的內(nèi)存也會少一點(diǎn)。

          Canvas 繪制的是“位圖”,在對位圖進(jìn)行縮放、旋轉(zhuǎn)等操作時,無法生產(chǎn)新的像素,因此會放大原有的像素填補(bǔ)空白,這樣會讓圖片顯得不清晰。矢量圖在移動,變換等操作中的開銷遠(yuǎn)大于位圖的,尤其是移動+縮放,因?yàn)闉g覽器要保證每一個邊都平滑無毛邊和過度。

          總結(jié) Canvas 的特點(diǎn):

          1. Canvas 繪制的是“位圖” 。
          2. 在元素特別多的情況下,Canvas 的性能會更高一點(diǎn)。
          3. Canvas 占用的內(nèi)存會更小一點(diǎn)。

          前面都是為了讓我們更清楚地了解 Canvas 是什么,接下來步入正題,了解下從 fabric 源碼學(xué)到的東西。

          Fabric 源碼

          官方定義:

          Fabric.js是一個功能強(qiáng)大且簡單的HTML5畫布庫,它給canvas上的元素提供了交互式對象模型。借助Fabric.js,你可以輕松地繪制各種圖形線條圖片,對它們進(jìn)行拖拽、旋轉(zhuǎn)、形變等操作,并且支持豐富的事件方法。

          • 倉庫地址:https://github.com/fabricjs/fabric.js
          • 文檔地址:http://fabricjs.com/

          適用場景:

          大量動畫,畫板,圖片處理等都可以使用這個庫。官方提供了大量的 demo:http://fabricjs.com/demos/ ,可以快速體驗(yàn)下。

          思維導(dǎo)圖:

          項目比較老舊,打包方式還是直接讀文件壓縮打包的。沒有 ES6,沒有 TS ,很多 IIFE 。

          整體代碼組織形式和關(guān)系:

          接下來,看一下它的核心技術(shù),源碼先從調(diào)用的方法來入手:

          const?canvas?=?new?fabric.Canvas("beidan",?options);?

          源碼如下:

          主要封裝了以下的方法:

          1. 外層包裹一個元素
          2. 創(chuàng)建 2個 Canvas ,一個 upperCanvas ,一個 LowerCanvas
          3. 初始化事件
          4. 處理 retina 屏

          創(chuàng)建 2 個 Canvas,一個 upperCanvas ,一個 lowerCanvas

          • lowerCanvas 只負(fù)責(zé)渲染元素;
          • 所有的交互,比如框選,事件處理都在 upperCanvas 上。

          我們可以看到 upperCanvas 等大小屬性都是一致的,坐標(biāo)也是可以一一對應(yīng)的。比如在 upperCanvas 上某個位置點(diǎn)擊了一下,就可以到 lowerCanvas 中相應(yīng)的坐標(biāo)去查找是否點(diǎn)擊了這個元素。

          如果你的畫布不需要任何交互,可以只渲染 ?LowerCanvas 元素。使用下面的代碼即可。

          new?fabric.StaticCanvas('domId',?options)

          處理 retina 屏

          retina 屏:一種具備高像素密度的液晶屏,同樣大小的屏幕上顯示的像素點(diǎn)由一個變成多個。如下圖:

          也就是在 retina 屏下,一個像素點(diǎn)會變成 devicePixelRatio 個像素點(diǎn),在同樣大小的屏幕上,高清顯示屏中的位圖會被放大,圖片會變得模糊。

          如下圖:

          所以,想要在高清屏下也正常展示圖片大小,我們可以生成比顯示它所需尺寸更大的視覺效果,然后縮小圖像,確保它的尺寸與我們想要的一致且高清。

          如何使用 canvas 實(shí)現(xiàn)呢?
          1. 放大 devicePixelRatio 倍 canvas 的寬高
          canvas.width?=?rect.width?*?devicePixelRatio;
          canvas.height?=?rect.height?*?devicePixelRatio;
          1. 將 canvas 按照理想尺寸繪制到屏幕上
          ?

          canvas 先按照自身的大小 canvas.width/height 繪制,之后由 style 指定的大小繪制到屏幕上。

          ?
          canvas.style.width?=?rect.width?+?'px';
          canvas.style.height?=?rect.height?+?'px';
          1. 使用 scale 去縮小尺寸,保證畫布內(nèi)容 scale 縮放回正常的寬高
          canvas.getContext("2d").scale(devicePixelRatio,?devicePixelRatio);

          用 Fabric 繪制出來的 canvas,如下:

          Fabric 處理 retina 源碼如下:【原理跟上面的三個步驟是一樣的】

          渲染優(yōu)化

          每一次改變會清空屏幕,然后重新繪制所有的對象。直接看源碼:

          比較關(guān)鍵的是這個 requestRenderAll() 方法

          回到我們一開始講的,如何讓瀏覽器在渲染的時候不卡頓,那就需要使用到 requestAnimationFrame 這個方法,這里做了兼容處理,如果不支持這個方法,則使用 setTimeout(callback, 1000 / 60) 來 hack 處理。

          requestAnimationFrame 方法:

          該方法告訴瀏覽器您希望執(zhí)行動畫并請求瀏覽器在下一次重繪之前調(diào)用指定的函數(shù)來更新動畫。該方法使用一個回調(diào)函數(shù)作為參數(shù),這個回調(diào)函數(shù)會在瀏覽器重繪之前調(diào)用。

          requestAnimationFrame 具備的優(yōu)勢:

          1. 經(jīng)過瀏覽器優(yōu)化,動畫更流暢
          2. 窗口沒激活時(Tab 或隱藏 iframe 里),動畫將停止,節(jié)省計算資源
          3. 更省電,尤其是對移動終端
          ?

          所以,fabricjs 中盡可能使用 requestRenderAll 替換 renderAll 方法。

          ?

          事件驅(qū)動

          事件處理都在 canvas_events.mixin.js 這個文件中。沒有什么晦澀難懂的代碼,大意就是劫持瀏覽器中的原生事件,然后模擬替換成他自己的事件。fire 觸發(fā)自定義事件。

          自己實(shí)現(xiàn)了一套自定義事件監(jiān)聽 EventEmitter。感興趣可以到文件 observable.mixin.js 中查看。

          繪制形狀對象

          網(wǎng)絡(luò)上有個將各個形狀對象的繼承,方法屬性都畫得很清楚,直接看圖片:

          總結(jié)

          fabricjs 在代碼組織,拆分上非常地清晰. 比較關(guān)鍵的幾個點(diǎn):封裝一整套事件系統(tǒng),分層canvas提升性能,優(yōu)化渲染方法,高清屏渲染,提供各種形狀的快捷繪制方法。這樣對于用戶來說,寫起 canvas 來說,就不再是“裸奔” 了。

          官網(wǎng)也附有很多 demo ,文檔。做為一個 canvas 庫來說,非常大地提升開發(fā)效率。

          不過,fabricjs 并未支持 OffscreenCanvas ,OffscreenCanvas 對于 canvas 的優(yōu)化來說,非常的有用。后續(xù)文章會講如何對 canvas 進(jìn)行系統(tǒng)優(yōu)化。關(guān)注查看~


          參考文章:

          • Differences between canvas. width and canvas. style. width and their applications
          • https://keelii.com/2021/05/08/fabricjs-internals/


          更多推薦


          前端推薦!3分鐘帶你了解開源圖片編輯器iDraw.js

          lerna + dumi + eslint多包管理實(shí)踐

          動態(tài)刻度可視化組件實(shí)現(xiàn)

          從零開發(fā)一款輕量級滑動驗(yàn)證碼插件(深度復(fù)盤)

          從零搭建全??梢暬笃林谱髌脚_V6.Dooring


          瀏覽 127
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  天天干天天噜天天操 | 日逼美女| 欧美性爱内射在线 | 成人色情做爱操女人视频 | 肏屄中国一级黄色 |