基礎部分
座標系
畫布座標、螢幕座標的概念
螢幕座標,絕對座標,類似於css中的絕對定位
畫布座標,類似於css中的相對定位,還要考慮縮放
幾何變換:平移、縮放、旋轉
平移,位置移動,形狀、相對位置不變
縮放,位置(相對螢幕座標)和大小都發生變化
旋轉,位置旋轉,形狀、相對位置不變
Canvas 中的所有幾何變換針對的不是繪製的圖形,而是針對畫布本身,也就是說,當移動、縮放、旋轉畫布之後,新的座標系只對新的操作生效
參考:
Canvas 幾何變換 - Canvas 基礎教程 - 簡單教程,簡單程式設計
Canvas 平移 translate() - Canvas 基礎教程 - 簡單教程,簡單程式設計
Canvas 縮放 scale() - Canvas 基礎教程 - 簡單教程,簡單程式設計
Canvas 旋轉 rotate() - Canvas 基礎教程 - 簡單教程,簡單程式設計
繪圖步驟
基礎繪圖三步法
獲取canvas物件
獲取上下文環境物件context
開始繪製圖形。
示例程式碼
const canvas = document。getElementById(“canvas”);const context = convas。getContext(“2d”);context。fillRect(100, 100, 50, 50);
通用繪圖步驟
儲存畫布(狀態),context。save();
畫布操作,context。transform(疊加) 或者 context。setTransform(不疊加)
設定樣式,繪製圖形
恢復畫布(狀態),context。restore();
圖形變換基本操作
清空畫布,context。clearRect
儲存狀態,context。save
畫布操作,doTransform
getShapeList and forEach
恢復狀態,context。restore
Canvas效能
Canvas的繪製和html的繪製是不一樣的,html的繪製是增量的,當變化時,只會重新繪製變化的部分,沒有變化的部分是不會重新繪製的,但是canvas不一樣,每次都是全量繪製的,如果一個canvas裡有很多圖形,當改變一個圖形時,需要重新繪製所有圖形才可以(當然,可以用clearRect擦除部分割槽域,但一般很少這麼用)。
瞭解canvas的繪製規則之後,就很容易發現效能問題,如果canvas上繪製了大量的圖形(成千上萬個),每次重繪就需要很長的時間,如果重繪的頻率很高,那麼就會有效能問題
那麼如何解決這個問題呢,目前有以下幾種方案
使用圖層
使用臨時圖層
使用webworker或wasm
使用webgl
使用圖層
圖層的概念來自於PS,每一個圖層都是一個canvas,既然在一個canvas上繪製太多圖形會有效能問題,那麼就分幾個圖層,每次僅重新繪製其中一個圖層,每個圖層的圖形都不會很多,那麼即使重繪的頻率很高,也不會有效能問題。圖層的概念圖如下:
這裡用背景顏色只是示意,實際上圖層都是透明
程式碼實現
用一個父元素作為容器,把所有的元素設定成一樣的寬高並放在裡面重疊。
使用臨時圖層
繪製是很耗效能的,如果每次都清空畫布然後重新畫一次,那麼效能會消耗很大(即使分了幾個圖層),我們應區分“變”與“不變”的部分,只對“變”的部分重新渲染,“不變”的部分不渲染,將經常變化的部分抽離到臨時圖層,這樣僅需要渲染臨時圖層,臨時圖層有幾種實現思路,一種是使用操作圖層(俗稱高效能圖層),一種是使用隱藏圖層(不繪製到介面上的)
高效能圖層
一般高頻(實時響應滑鼠、鍵盤等事件)的操作會放在高效能圖層,等操作完成之後,再將最終結果儲存到其它圖層,比如繪製、拖拽、縮放一個(或一批)shape
隱藏圖層
有些圖層是不用給使用者看的,這些canvas僅存在於記憶體中,不會插入html的dom中,用完就銷燬,比如常見的canvas to image。
還有一種實現方式是離屏渲染(OffscreenCanvas),先在一個offCanvas操作,然後再將結果渲染到介面上(有點像虛擬dom操作),一般會結合webworker或webassembly
const canvas = document。createElement(“canvas”);const context = canvas。getContext(“2d”);// 繪製圖片,或其它操作context。drawImage();// 轉成base64圖片convas。toDataUrl();
使用webworker或wasm
影響canvas效能的除了繪製頻率,還有一個重要的是畫素點操作,一般影象處理會涉及到大量的畫素點操作,如果放在主執行緒計算,那麼會卡住其它操作,造成頁面卡頓,特別影響使用者體驗,這些涉及大量計算的一般會單獨開個執行緒來操作,而在瀏覽器中有這個能力的就只有webworker了。
有了webworker可能還不夠,因為始終是在js上執行,js執行效率天生就比其它語言慢,所以一般的會使用webassembly,執行效率比js快很多,而且還能用到更豐富的影象處理庫
使用webgl
如果還有更高的效能要求,那麼普通的2d canvas可能就無法滿足了,這個時候可以使用webgl,效能更高(當然學習成本也更高),再結合wasm,就可以有無限想象力了,鼎鼎大名的figma就是用webgl + wasm(rust)實現的,另外google doc線上文件也使用了webgl,飛書文件將來也會替換成wegbl,基於瀏覽器的渲染始終有諸多限制,一般有能力的都會實現自己的渲染引擎。
業務中圖片縮放的實現設計
假設canvas大小為(867,350)
圖片的大小為(768,576)
將上面這張圖片放到canvas中,圖片貼邊處理,也即圖片太大就縮小,圖片太小就放大。那麼我們如何實現這種效果呢?
總結一下,總共分為幾步:
計算畫布大小和圖片大小
計算如果將圖片以原圖大小放入畫布中心,左上角的座標
將畫布座標移動到圖片中心點
將畫布放大或縮小(scale=Math。min(畫布寬度/圖片寬度,畫布高度/圖片高度))
重新移動畫布座標到左上角
繪製圖片(或圖形)
canvas的執行細節如下: