關於Canvas的一些經驗

turingbooks發表於2020-04-07

來源:圖靈社群

圖靈發言
TEAP是什麼?TEAP是Turingbook Early Access Program的簡稱,即早期試讀,它公佈的是圖靈在途新書未經編輯的內容。一本書的寫作週期約為3-6個月,如果在寫作的時間裡,作者就能夠與讀者進行溝通和交流,讀者可以提前閱讀將來才能出版的內容,作者也能收穫寶貴的反饋意見。

本次TEAP公佈的書是《論道HTML5》,歡迎在此與作者秀野堂主交流。本篇內容選自書中第4章第8節。

本小節是作者在開發過程中的一些粗淺經驗與心得,都是經過具體事務處理後得到的教訓,在此作一個總結,希望在開發實踐中能夠對讀者有一些幫助。

1. 回答兩個問題

還記得我們在第三章的Cavnas API小節向大家提的問題嗎?

問題1:Canvas是不是透明的?

問題2:Canvas可不可以互相堆疊在一起?

在這裡,我們要向一些已經學習了Canvas的讀者們鄭重提醒,在設計應用的時候,做足相關的技術理論準備工作,在一些基本的問題上花費一些時間,可以避免以後的重大缺陷和創造更加令人驚豔的效果。

這裡,我們給出兩個問題的答案:

  • Canvas是沒有背景色的,也就是說:Canvas是預設完全透明的,就像一塊玻璃,而不是一塊畫布,如果非要理解成畫布,那也應該是一塊透明的塑料畫布。

  • Canvas在瀏覽器中納入了DOM體系,是可以通過CSS 的Position和z-index值來操作任意多個Canvas堆疊在一起,合成效果,就像操作DIV標籤一樣。

結論:

根據上面兩個答案,我們可以提出一個解決方案:當一個Canvas有過多的繪圖任務時,效率會越來越低下。我們可以利用Canvas的透明屬性和可以堆疊的特徵,剝離相關資源和繪圖任務,使作品的結構更加清晰,更加適合團隊開發。

2. 避開浮點運算

當你的作品中有大量的浮點運算時,效率必然就會下降很多。我們建議:對要計算的值 先取整後再繪圖,可以提高效率。這個小技巧在開發手機遊戲或者需要大量渲染而對圖形質量的要求可以降低的話,是非常有用的。

3. 資源載入與雙緩衝問題

在網上常見的Canvas開發教程中,有些程式碼看上去沒有問題,但是實際上不能在工程中直接使用的,這樣的程式碼只有教育意義。

例如:

var ctx = mycanvas.getContext('2d');
var img = new image();
img.src=”1.png”;
ctx.drawImage(img);

在開發Canvas遊戲時,常常會有很多資源需要載入,而且反覆DrawImage時,如果沒有做好資源載入控制將帶來非常嚴重的顯示問題(有時候顯示,有時候不顯示,在動畫情況下會閃爍)。因此,我們建議在開發中使用陣列或JSON等序列化所有的遊戲資源,然後將相關資源一次性建立相關物件,儲存在記憶體中,以便在下載完畢後,頻繁讀取時不必再去讀圖片資源,而是直接讀記憶體中的資源,這樣一來,一是速度快了許多,二是穩定性有了保障。

而通常情況下,我們把上面所說的這種方法稱之為雙緩衝繪圖,也是遊戲開發中最常見的方法。

示例程式碼:

var game = {};//遊戲物件
game.imgResource = [
       {id: 'a1', src: 'image/1.jpg'},
    {id: 'a2', src: 'image/2.png'},
    {id: 'a3', src: ' image/3.png'}
];//遊戲物件中的圖片資源物件序列化
var source={};//資源物件
source.imgs = {};//開闢圖片記憶體緩衝區
for(var i ==0;i<game.imgResource.length;i++){//開始資源預載
    source.loadImg(game.imgResource[i].id, game.imgResource[i].src);
}
source.loadImg = function(id, src) {//全部存入記憶體中
    source.imgs[id] = new Image();//開始存入記憶體中
    source.imgs[id].src = src;//指定資源路徑
};

當圖片全部載入到記憶體後,就可以從記憶體中讀取相關的資源進行繪圖操作了,而這時,已不會發生圖片閃爍,忽隱忽現等問題了。這是Canvas新手需要注意的問題。

4. 圖片抗鋸齒的原理及辦法

圖片的抗鋸齒處理可能是很多人都關心的內容,一些有經驗的開發人員都在實際開發和專案中遇到了這些問題而苦於沒有解決辦法。本小節將對部分瀏覽器中Canvas裡繪入圖片後旋轉產生鋸齒的現象進行解釋、處理和優化,達到抗鋸齒後的完美效果(此問題,在非Safari的移動裝置上尤其突出)。

概念簡介

在介紹抗鋸齒前,我們需要讀者大概瞭解幾個概念常識:

  • 點陣圖(又叫:點陣圖、光柵圖、畫素圖),簡要的講,就是以密集的點組成的圖,從圖形說的角度說,影像是光的複雜顯示,我們可以把一幅影像的每個點都看成是一個光的格子,我們通常稱之為光柵,所以又有光柵圖片一說。[ 這裡主要向非專業計算機圖形學人員進行概念的口頭普及,若有不嚴謹處,望方家們見諒。]

  • 向量圖(又叫:向量圖),簡要的講,就是以各種複雜資訊描述而成的圖,記錄的內容是點、線、面、顏色等資訊。這樣的圖,放大,縮小,都不會造成鋸齒,也不會失真,是最適合做電子地圖、標識等顯示的方案。

  • 鋸齒,就是點陣圖在某些角度上顯示的時候,圖片邊緣出現毛邊,類似鋸齒一般,放大後仔細觀察,就像是一個一個小格子。

  • 抗鋸齒:就是讓鋸齒消失,在各種角度下,點陣圖都平滑顯示。

鋸齒問題的產生

關於抗鋸齒,有些讀者可能已經查閱了大量的資料,研究了大量的演算法,但是卻一無所獲,在這裡,我們給出一個旋轉的抗鋸齒案例,簡單而易操作,希望能拋磚引玉。

我們在一次工程案例中,需要動態旋轉一個圓形圖片,想當然的以為可以非常簡單的實現,結果在測試中發現鋸齒很嚴重。除在移動裝置iPhone,iTouch,iPad上使用Safari看起來比較平滑能夠接受之外,在其它任何瀏覽器上測試時,都有鋸齒。

鋸齒問題的分析

在計算機影像處理中,一幅點陣圖的平面角度動態變化(旋轉)顯示是比較有意思的,圖片在旋轉後,每個點陣圖中的點都發生了變化,例如:

圖的面積是:[x0,y0]->[xn,yn],圖中某個點在未旋轉前是:1,1,經旋轉角度13度後,變化如下:

1->7*(Math.PI * 13/180)

這個點變成了1.588

經過這次計算,每個點都不能互相對應上,照成有的點溢位在不應該顯示的地方,而有的點由於位置不對,不再顯示。而使用軟體渲染器的時候就算是雙線性插值也不能完全保證(特別是有弧線的)能夠一一對應。這就是鋸齒產生的原因。

鋸齒問題的解決辦法

既然我們找到了原因,我們就可以想出解決辦法。一般來說解決的辦法有至少四種:

  1. 使用硬體渲染(以蘋果公司移動裝置中的safari為代表)。

  2. 使用向量圖(如果可以替代的話)。

  3. 影像以2的次方放大,儘可能的保證渲染器取樣到原圖而不是插值。

  4. 以複雜的抗鋸齒演算法來解決這個問題(計算量大,而且非專人員看不懂,更別提使用了)。

看到這裡,我想讀者們已經明白了,我們要採用的,就是第三種辦法。

假設:要在裝置中顯示一幅200*200的圖,並旋轉。

則:

使原圖增大一倍,達到400*400,然後再將此圖多設1個畫素,相當於手動新增中心圍繞點(最關鍵的一步),為什麼要新增1個畫素的中心圍繞點呢?這是為了方便渲染器計算用的。一幅400*400畫素的圖,如果旋轉起來,半徑是多少?答:不是200,而是約等於199.5。因此,我們給此圖再手動加一個空白畫素,使旋轉的半徑為整數:200。增加了中心旋轉點後,一方面提升了計算效率,另一個方面,使渲染器的每個點都能夠比較準確的落在原點上。因此,源圖的尺寸達到401*401,至此,鋸齒問題能夠得到圓滿的解決。

本小節是抗鋸齒技巧中的一個,具體例項程式碼在第五章HTML5和移動網際網路開發部分中的指南針例子裡,請讀者自行參考。

5. 粒子系統的使用

什麼是粒子系統?很難直接描述。但是大家都看到過火焰,火焰的狀態是沒有固定形勢的,在空氣中,火焰翻騰跳躍。如何用HTML5的Canvas去描繪一幅火焰呢?

辦法有兩種 :一種是用多幅圖片,幀式快速播放,可以給人一種火焰在跳動的感覺。但是這樣生成的效果非常簡單,火焰的形態也很枯躁。另一種 ,就是我們這裡要介紹的粒子系統,完全依賴計算和程式設計,模擬出火焰的形態,給人強烈的真實的感受。

粒子系統原來是計算機3維圖形學中的技術,像火焰、爆炸、流水、雲等,都可以模擬的非常真實。

我們在實際的工作中,限於移動裝置的環境,我們不能進行大量的計算,因此,建議讀者在實際開發中,做好衡量和取捨,採取適當的粒子數量。既保證效果,又能讓程式高效的執行。

有一點是可以我們是確定的:採用了適應粒子系統效果的遊戲和應用,可以節省大量的圖片資源、載入時間短、幀數高、速度快。

因此,我們建議有大量圖片去堆出效果的遊戲和應用採用粒子系統,事半功倍。本書作為基礎進階資料,不對粒子系統進行全面的講解,有興趣的讀者可以去研究相關的資料。


相關文章