HTML5 快速學習二 Canvas

MiroYuan發表於2014-11-25

本篇文章開始講解HTML5的核心功能之一:Canvas

通過Canvas可以動態生成和展示圖形、圖表、影象以及動畫。

Canvas API功能非常多,我們將討論最常用的功能。

我們先新建一個canvas看看。

我們給canvas加一個邊框,這樣比較方便看。

可以看到, canvas會建立一塊矩形區域,預設情況下生成大小是300*150畫素。

在頁面中加入canvas後,我們便可以通過js來自由地控制她。

例如 新增圖片、線條以及文字,也可以在裡面繪圖,甚至加入高階動畫。

Note

把canvas當作一個普通的標籤,可以通過應用CSS的方式來改變樣式,而且一些CSS屬性還可以被canvas內的元素繼承。

例如字型樣式,在canvas內新增文字,其樣式預設是同canvas元素本身是一樣的。

文章提綱

  • 要點
  • 理論基礎/前置條件
  • 詳細步驟
  • 總結

要點

掌握使用canvas API的重要流程

掌握常用的canvas API:例如moveTo, lineTo, beginPath, closePath,stroke,fill等

充分理解例子

理論基礎 -- canvas座標

如下圖,canvas中的座標是從左上角開始,x軸沿著水平方向(按畫素)向右延伸,y軸沿垂直方向向下延伸。

最左上角座標為 (0,0) 的點為原點。

詳細步驟 -- 使用HTML5 canvas API

檢測瀏覽器支援情況

我們做兩件事

  1. 我們用一段script判斷瀏覽器支援情況。

    如果不支援可以將提示資訊顯示在特定的位置。

    如下圖,我們用了一個id="support"的div來顯示提示升級的資訊。

  2. 我們在canvas中寫入一段替代內容.

    如下圖,如果不支援,canvas會顯示替代內容。

瀏覽器支援

把IE調成IE7模式測試下不支援的情況:

 

利用canvas畫一條對角線

對上面的例子做一些修改

在canvas中繪製一條對角線

根據上面的js程式碼,歸納出使用canvas API的重要流程

  1. 根據canvas ID值獲取canvas物件訪問權,接著定義一個context變數,呼叫canvas物件的getContext方法。
  2. 基於這個context執行動作(這裡是畫一條對角線)
  3. 通過context.stroke()完成線條的繪製。

Note

這裡有一個坑。我原來將設定canvas長寬放在了style裡面。如下圖。

出現問題的原因:

canvas的width和height是畫布的實際寬度和高度,繪製的圖形在這個畫布上面。

canvas的style的width和height是canvas在瀏覽器中被渲染的高度和寬度。

因此需要注意設定寬度時要在外面設定。

使用變換(transformation)畫對角線

下面來看canvas上繪製影象的另外一種方式:使用變換(transformation)。

transformation是實現複雜canvas操作的最好方式(就單個上面繪製對角線來說看起來是更加複雜了點)

理解 變換(transformation):

把它當成是介於開發人員發出的指令和canvas顯示結果之間的一個修正層 (modification layer)

注意 不管在開發中是否使用變換,修正層始終存在。

每個繪製操作的結果顯示在canvas上之前都要經過修正層去修正。

雖然這麼做增加了額外的複雜性,但卻為繪製系統新增了更為強大的功能。

Note

不在程式碼中呼叫變換函式並不意味著可以提升canvas的效能。

canvas在執行的時候,變換會被呈現引擎隱式呼叫,這與開發人員是否直接呼叫無關。

可重用程式碼的一條重要建議:

一般繪製都應從原點開始,應用變換(縮放,平移,旋轉等),然後不斷修改程式碼直至達到希望的效果。

示例

這個程式碼的結果和上面是一模一樣的。

大家注意這兩種程式碼的差別:

對第二種方式, translate(70,140) 代表將原點移到 (70,140) 這個位置。

也就是說,接下來所有操作都是相對於 (70,140) 這個位置來操作的。

第一種情況是(70,140)à(140,70),

第二種情況是(0,0)à(70,140)à(70,-70)

第二種情況的(70, -70)是相對於新的原點(70,140)點來說的,相對於一開始的原點座標是(70+70,-70+140),很容易看到這兩種情況的結果是等價的,理解了嗎?

大家體會一下。

我們歸納一下上面的操作:

  1. 根據canvas ID值獲取canvas物件訪問權,接著定義一個context變數,呼叫canvas物件的getContext方法。
  2. 儲存尚未修改的context, 這樣即使進行了繪製和變換操作,也可以恢復到初始狀態(通過後面的restore函式)
  3. 通過translate來移動原點,這個上面已經解釋過了。
  4. 基於移動過的context執行畫線動作
  5. 通過context.stroke()完成線條的繪製。
  6. 最後,恢復context至原始狀態,這樣後續的canvas操作就不會被剛才的平移操作影響了。

畫樹

現在學習稍微複雜點的圖形。

前面繪製的一條對角線算是一條簡單路徑。

實際上路徑可以很複雜:多條線、曲線段、甚至是子路徑。

如果想在canvas上繪製任意形狀,那麼你需要重點關注路徑API

按照慣例,不論開始繪製何種圖形,第一個需要呼叫的就是beginPath, 對於canvas來說,beginPath函式的最大用處是canvas需要據此來計算圖形的內部和外部範圍,以便完成後續的描邊和填充。

路徑會跟蹤當前座標,預設值是原點。

呼叫beginPath之後,就可以使用context的各種方法來繪製想要的形狀了。

到目前為止已經使用了幾個簡單的context路徑函式。

moveTo(x,y)

lineTo(x,y)

上面兩個函式的區別是:moveTo就像是提起畫筆,移動到新位置;

而lineTo告訴canvas用畫筆從紙上的舊座標畫條直線到新座標。

注意,不管呼叫的是哪一個,都不會真正畫出圖形,因為我們還沒有呼叫stroke或者fill函式。

目前我們只是定義路徑的位置,以便後面繪製時使用。

另外再介紹一個路徑函式closePath, 這個函式和lineTo很像,唯一的差別是會將路徑的起始座標 自動作為 目標座標。

clothPath還會通知canvas當前繪製的圖形已經完全閉合或者形成了完全封閉的區域,這對將來的填充和描邊都非常有用。

此時,可以在已有的路徑中繼續建立其他的子路徑,或者隨時呼叫beginPath重新繪製新路徑並完全清除之前的所有路徑。

1.繪製樹冠

繪製樹冠的函式

 

為了直觀的顯示圖線的走勢,我畫了個從開始點到頂點的草圖,如下

在canvas上畫樹的函式:

最終結果如下

下面我們對樹冠做一些美化,在stroke之前新增如下程式碼

變成了更粗更平滑的棕色線條。

進一步美化,將閉合路徑內部填充為綠色

 

注意,右邊的邊框也變細了。

當我們採用先描邊後填充的方式,會填充一半的邊框。

如果要不填充邊框,需要採用先填充後描邊的方式,如下。

2. 利用fillRect畫樹幹(填充矩形區域)

我們先把translate的數值改一下,讓出樹幹的位置。

context.translate(130,150);

通過fillRect(x, y, 寬, 高)來畫出樹幹。

注意,這段要在context.restore();前面,否則畫的位置就錯了。

最終結果:

Note

fillRect(x,y,width,height)

總結

大家初步可以看到canvas的威力,可以不用藉助第三方技術進行繪圖。

當然目前畫的東西還比較簡單,下篇文章將會在這棵樹的基礎上加入其他元素和特殊效果,完成一幅雨水動畫效果的林蔭小道圖。

好了,今天就到這裡,歡迎大家多多評論,讓下一篇文章更好:)

 

相關文章