日韩av黄I国产麻豆传媒I国产91av视频在线观看I日韩一区二区三区在线看I美女国产在线I麻豆视频国产在线观看I成人黄色短片

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 前端技术 > HTML >内容正文

HTML

【Fanvas技术解密】HTML5 canvas实现脏区重绘

發布時間:2023/12/13 HTML 47 豆豆
生活随笔 收集整理的這篇文章主要介紹了 【Fanvas技术解密】HTML5 canvas实现脏区重绘 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

先說明一下,fanvas是筆者在企鵝公司開發的,即將開源的flash轉canvas工具。

?

臟區重繪(dirty rectangle)并不是一門新鮮的技術了,這在最早2D游戲誕生的時候就已經存在。

復雜的術語或概念就不多說,簡單說,臟區重繪就是每一幀繪制圖形界面的時候,只重新繪制有變化的區域,而不是全屏刷新。很明顯,這肯定能帶來性能的提升。

舉個例子,看下邊兩個圖:

?

假設這里是動畫的連續2幀,那么從第一幀到第二幀,其實變化的只有蝴蝶的區域。那么所謂的臟區就是兩個圖片的紅色框之和,要把上一幀的蝴蝶擦掉,然后把新區域的蝴蝶位置也擦掉,接著才能繪制新的背景和蝴蝶。這相比整屏重繪,重繪的面積小了幾十倍,由于canvas 2d使用的是CPU處理,那么相應地,CPU處理的像素個數就少了很多倍,順理成章,動畫的效率就會提高。

?

看起來非常簡單,大概來說,只需要2步:

1、找出這一幀變化的矩形區域;

2、利用canvas的api實現臟區重繪。

?

但是,問題來了,怎么計算變化區域呢?canvas又是否提供了現成的接口呢?我們拿上述蝴蝶的例子,逐步來看看。

?

首先,我們看關于臟區的計算。

如果動畫非常簡單,沒有使用“顯示列表”,所有圖案都是一層繪制的,那么“也許”繪制者,也就是開發者了,可能會知道蝴蝶的位置,然后手工指定重繪的區域。呃。。。等等,好像有點什么問題,不可能每次都手工指定重繪的區域!!!

再看看Fanvas里的情況,Fanvas采用了顯示列表,把圖案拆分為多個元件,元件和元件之間以“顯示列表”的形式組織起來,這參考了Flash的技術。這里,蝴蝶被封裝為一個Shape,蝴蝶在畫面飛舞,抽象為Shape在父元件中移動、旋轉。最初,在Shape中繪制蝴蝶的時候,可能占據的矩形區域是(x:0,y:0,width:100,height:50),這里參考的是Shape內部的坐標系(還沒放到舞臺上)。然后,蝴蝶被添加到舞臺上時,需要位移和旋轉,例如做了(x:400,y:100)的位移,和旋轉了60度。這時候如何計算新的矩形呢?

?

這個過程其實就是局部坐標系映射到全局坐標系的問題,涉及到矩陣計算,可以參考我之前寫的文章,這里就不多說了。http://km.oa.com/articles/show/238103。另外,提一下,這里其實還有一個難點,初始繪制時(x:0,y:0,width:100,height:50),這個矩形是如何計算得到的呢?如果繪制的是一個圖片,那當然好計算;如果是一系列的矢量線條,這個就略麻煩了,不過這個不在這里討論了,因為Fanvas是Flash導出Canvas動畫,在導出的時候Flash自帶了這個矩陣信息。

?

上述的計算都在一個前提情況下:我們已知蝴蝶是唯一一個變化的元件,但在實際動畫過程中,如何自動識別變化的內容呢?

要從動畫的原理說起,動畫過程無非分為4種操作:

1. 新建一個元件(例如蝴蝶),添加到舞臺上;

2. 移動、旋轉、放縮原有的元件;

3. 刪除已有的元件;

4. 修改元件的遮罩關系,這點有點特殊,如果對flash動畫不熟悉的同學可能不大理解,不過不重要,我們知道有這回事就可以了,不影響文章的繼續閱讀。

?

那么,在Fanvas中,我們就需要對上述4種情況分別處理。

1. 新建:只有1個臟矩形,就是這個元件本身;

2. 移動/旋轉/放縮:元件上一幀的矩形區域是臟區,新一幀的矩形區域也是臟區;

3. 刪除:跟新建情況一樣;

4. 遮罩變化:跟2一樣。

?

理清楚這些細節之后,如何實現就比較好辦了,無非就是每一幀繪制前把臟區列表情況,然后計算出所有臟區矩形,再開始繪制。

?

接著,我們再來看第二步,canvas如何具體操作,是否有臟區重繪接口?

?

其實,canvas并沒有真正的臟區重繪接口,不過有一個clip,這個一般用于實現遮罩,不過也可以取巧的用來實現臟區重繪。經筆者測試,簡單使用clip雖然性能優化不是太明顯,但還是有20%的提升的。再復雜一些,當然大家可以自行根據臟區列表,重寫每個元件的繪制方法,自行實現臟區重繪,不過筆者估計啊,js寫這么多邏輯,最終還是吃力不討好。

我們來看看代碼:

for (var i = 0; i < dirtyRectList.length; i++) { var rect = dirtyRectList[i]; ctx.clearRect(rect.x, rect.y, rect.width, rect.height); } ctx.beginPath(); for (var i = 0; i < dirtyRectList.length; i++) {var rect = dirtyRectList[i];ctx.rect(rect.x, rect.y, rect.width, rect.height); } ctx.clip();

?

相信變量名已經很明顯的暴露了自己的用途,大家應該明白,實現臟區重繪非常簡單,只需要在全部繪制前加那么一段clip,搞掂。

不過啊,這里可有一個很大的坑,估計有同學也知道。

正如上圖所示,會出現一些1px白線或者沒清干凈的bug,尤其是舞臺本身有拉伸的情況下,這種bug更明顯。經過筆者多次摸索,大概搞清楚了,主要就是臟區要算仔細(如果舞臺有拉伸,很容易算出來有1、2px差別),畫面要等比例拉伸,另外就是清除和重繪時,大方點,給1px的放寬。

最后變成:

for (var i = 0; i < dirtyRectList.length; i++) { var rect = dirtyRectList[i];ctx.clearRect(rect.x-1, rect.y-1, rect.width+2, rect.height+2);}ctx.beginPath(); for (var i = 0; i < dirtyRectList.length; i++) {var rect = dirtyRectList[i];ctx.rect(rect.x-1, rect.y-1, rect.width+2, rect.height+2); }ctx.clip();

?

至此,Fanvas臟區重繪的秘密就徹底曝光了。。。

最后來看看實際的效果(第一張是沒有使用臟區重繪,第二張使用臟區重繪):

??

?

當然,這并不是每個動畫都有效果,因為一些動畫本來就大范圍變化,所以Fanvas針對這些情況也做了兼容處理,如果發現臟區太多或者面積太大,就繼續使用原來的全屏重繪。

轉載于:https://www.cnblogs.com/kenkofox/p/4506592.html

總結

以上是生活随笔為你收集整理的【Fanvas技术解密】HTML5 canvas实现脏区重绘的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。