前端“油畫設計師”——雙緩存繪制與油畫分層機制


背景

Canvas在圖像處理、繪制渲染上有一些得天獨厚的優勢。但是當我們當前展示的內容中在主題內容變化不大的情況下,會有一些小部分內容的變化,在頁面刷新或者滾動的時候,一幀中會有很多復雜內容元素的圖畫運算,重新對頁面元素繪制會導致CPU使用率飆升。

而重新繪制的過程,實質上是一個不斷刮白-重畫的過程。但在屏幕上完成這一系列操作是需要一定時間的,而且屏幕上的圖形越復雜,所花的時間就越長,我們肉眼可見的刮白-重畫操作,在使用過程中就會讓就會直接感覺到屏幕的閃爍。

重繪帶來的性能負擔和閃爍的問題,會給使用者帶來較差的使用體驗。為了更好的優化這個兩個問題,出現了雙緩存畫布和油畫分層的繪制方法。而本節內容我們也將從電子表格技術出發,為大家揭秘在電子表格技術中雙緩存與優化技術的具體應用。

雙緩存畫布

現在我們有一幅圖需要放在Canvas中,使用drawImage()方法,有三種寫法:

// 將image放到目標canvas指定位置

void ctx.drawImage(image, dx, dy); 

// 將image放到目標canvas指定位置,指定寬高渲染

void ctx.drawImage(image, dx, dy, dWidth, dHeight);

// 將image裁剪之后放到目標canvas指定位置,指定寬高渲染

void ctx.drawImage(image, sx, sy, sWidth, sHeight, dx, dy, dWidth, dHeight);

第一種方法只是把圖片原樣放到Canvas中,第二種方法指定寬高就意味着放大或者縮小圖片后再放進去,第三種是將圖片裁剪后再放大或者縮小放到canvas中,這三種寫法操復雜度作依次增加,性能開銷也隨之增大。

而如果使用離屏渲染(即我們所說的雙緩存畫布),我們可以預先把圖片裁剪成想要的尺寸,然后將該內容保存起來,繪制的時候直接使用第一種寫法直接將圖片放入Canvas中。

// 在離屏 canvas 上繪制

var offscreencanvas = document.createElement('canvas');

// 寬高賦值為想要的圖片尺寸

offscreencanvas.width = dWidth;

offscreencanvas.height = dHeight;

// 裁剪

offscreencanvas.getContext('2d').drawImage(image, sx, sy, sWidth, sHeight, dx, dy, dWidth, dHeight);

// 在視圖canvas中繪制

viewcontext.drawImage(canvas, x, y);

雙緩存畫布技術的核心在於系統需要在內存中開辟一塊與當前畫面等大的“邏輯屏幕“。我們的畫圖和動畫操作都會先作用於這塊”邏輯屏幕“中,當一個操作在這塊”邏輯屏幕“上完成之后,再把整塊”邏輯屏幕“投放到我們的屏幕上。

(離屏渲染原理示意圖)

在這樣的過程之下,我們是無法看到整個圖形在屏幕上的重繪過程,從而解決了閃爍問題。就好像看動漫一樣,不用雙緩存技術,就是畫一幀看一幀,肯定會卡頓。而用了雙緩存技術,會事先把每一幀畫好,不斷翻動展示出來。

(逐幀動畫)
Canvas為此提供了OffscreenCanvas方法,用來構建一個可以脫離屏幕渲染的canvas對象,它在窗口環境和web worker環境均有效。對於一些渲染,如果創建 Image 再進行渲染,會消耗大量 CPU,但用離屏渲染,實測在高頻事件中 CPU 使用率減少了一倍之多。

油畫分層繪制

分層渲染來處理畫面動畫的思路並不是現在才有的,從非物質文化遺產皮影戲、套色印刷技術,到現在的音影工業等眾多領域都有頻繁出現, 而這種思想在Canvas中也處在基石的地位。

(分層渲染原理示意圖)

Canvas分層的思想是,動畫中每種元素,對渲染和動畫的要求是不一樣的。

用下圖舉個例子,在這張圖片中除了貓本身在運動外,背景以及下方的文字都是靜止重復的。

(油畫分層機制示意圖)

按照分層的邏輯,我們需要頻繁更新繪制的只有最上方的貓咪。這個方法類似油畫的繪制,所以也被稱為油畫分層機制。使用這個方法結合雙緩沖技術可以有效的將重復繪制的內容分流到屏幕外的畫布上,然后再根據我們的需求將屏幕外圖像渲染到主畫布上,省去了頻繁生成重復部分的步驟。

技術應用落地

在實際應用中需要在前端對復雜內容進行渲染或者處理大量數據時,為了更好地對性能進行優化,現在已經有很多項目實際采用了Canvas的雙緩存畫布和油畫分層技術。我們在做電子表格技術選型時也考慮到了這些問題,在電子表格應用項目中,我們動輒需要處理百萬數量級數據內容,這種情況下瀏覽器對表格內容渲染和數據處理的性能就顯得無比重要。

上圖是純前端電子表格中50000*20=100000個數據,處理只需要0.038s。在該純前端電子表格中,整個繪制引擎根據油畫繪制原理,分為主體圖層和裝飾圖層,主題圖層將會渲染持久的,不會輕易改變的元素,例如背景,單元格,表格線等。而裝飾圖層則會渲染常變性元素,例如選擇框,拖拽框,懸浮效果等。在下圖中第一層到第四層都是主體圖層的內容,第五層是裝飾圖層。

除此之外整個的繪制過程並不是從數據層(Model)直接到視圖層(View)的。而是根據表格內容的特殊性,實現了根據視圖層形狀,從數據層組合出一層專屬視圖層的視圖數據(ViewModel),再配合前文提到的雙緩存畫布繪制機制,完成整個表格按需繪制的需求,並緩存繪制結果,進一步提升繪制性能。

主體圖層不是直接繪制在用戶能看到的主畫布上,而是繪制在一個看不見的緩存畫布上。在需要渲染時,只需要講緩存畫布的內容克隆到主畫布上,再附加上裝飾圖層元素

這樣,當表格需要更新時候,比如單元格背景改變,只需要在克隆緩存畫布后重繪對應單元格內容即可。

而當表格向下滾動時,表格滾動結束,需要重繪,主畫布會被清空,然后從緩存畫布中根據行為上下文進行畫布偏移,將偏移后的圖層直接繪制在主畫布上,隨后在主畫布上繪制偏移后的剩余部分,最后更新緩存。

使用緩存畫布和油畫分層機制,大大提升了繪制性能,使整個滾動過程更加流暢、順滑。

覺得不錯給點個贊吧~后續還會為大家帶來更多技術揭秘和有趣內容。

拓展閱讀


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM