本篇文章開始講解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
檢測瀏覽器支持情況
我們做兩件事
-
我們用一段script判斷瀏覽器支持情況。
如果不支持可以將提示信息顯示在特定的位置。
如下圖,我們用了一個id="support"的div來顯示提示升級的信息。
-
我們在canvas中寫入一段替代內容.
如下圖,如果不支持,canvas會顯示替代內容。
瀏覽器支持
把IE調成IE7模式測試下不支持的情況:
利用canvas畫一條對角線
對上面的例子做一些修改
在canvas中繪制一條對角線
根據上面的js代碼,歸納出使用canvas API的重要流程。
-
根據canvas ID值獲取canvas對象訪問權,接着定義一個context變量,調用canvas對象的getContext方法。
-
基於這個context執行動作(這里是畫一條對角線)
-
通過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),很容易看到這兩種情況的結果是等價的,理解了嗎?
大家體會一下。
我們歸納一下上面的操作:
-
根據canvas ID值獲取canvas對象訪問權,接着定義一個context變量,調用canvas對象的getContext方法。
-
保存尚未修改的context, 這樣即使進行了繪制和變換操作,也可以恢復到初始狀態(通過后面的restore函數)
-
通過translate來移動原點,這個上面已經解釋過了。
-
基於移動過的context執行畫線動作
-
通過context.stroke()完成線條的繪制。
-
最后,恢復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重新繪制新路徑並完全清除之前的所有路徑。
繪制樹冠的函數
為了直觀的顯示圖線的走勢,我畫了個從開始點到頂點的草圖,如下
在canvas上畫樹的函數:
最終結果如下
下面我們對樹冠做一些美化,在stroke之前添加如下代碼
變成了更粗更平滑的棕色線條。
進一步美化,將閉合路徑內部填充為綠色。
注意,右邊的邊框也變細了。
當我們采用先描邊后填充的方式,會填充一半的邊框。
如果要不填充邊框,需要采用先填充后描邊的方式,如下。
2. 利用fillRect畫樹干(填充矩形區域)
我們先把translate的數值改一下,讓出樹干的位置。
context.translate(130,150);
通過fillRect(x, y, 寬, 高)來畫出樹干。
注意,這段要在context.restore();前面,否則畫的位置就錯了。
最終結果:
Note
fillRect(x,y,width,height)
總結
大家初步可以看到canvas的威力,可以不用借助第三方技術進行繪圖。
當然目前畫的東西還比較簡單,下篇文章將會在這棵樹的基礎上加入其他元素和特殊效果,完成一幅雨水動畫效果的林蔭小道圖。
好了,今天就到這里,歡迎大家多多評論,讓下一篇文章更好:)