iT邦幫忙

DAY 10
5

HTML5試試看系列 第 10

[HTML5試試看-10] canvas 與 2d context(續)

今天還是繼續昨天的主題,測試一下canvas 2d context。除了一些基本屬性及狀態的管理,大致上測試一下繪圖、變形、文字跟影像這四個部份的功能。
所以,我就以基本屬性使用方式及狀態管理、簡單的繪圖測試、變形、文字、影像等主題,寫幾個簡單的測試來看看他的功能。希望能在5000字的限制中做完。最完整的東西還是在[http://www.w3.org/TR/2dcontext/ HTML Canvas 2D Context]文件中,有問題的話還是去看看比較好。

基本屬性及狀態管理
由於所有的屬性在context中都是global的,所以改動了一個或多個屬性,會影響接下來所有的繪圖動作。有了save()跟restore()以後,只要在改動前使用save()然後在需要恢復到改動前的狀態時使用restore(),就可以很方便地管理狀態。

save()及restore()可以管理的狀態包含:strokeStyle, fillStyle, globalAlpha, lineWidth, lineCap, lineJoin, miterLimit, shadowOffsetX, shadowOffsetY, shadowBlur, shadowColor, globalCompositeOperation, font, textAlign, textBaseline等,還有canvas內影像對應的transform matrix。

一些顏色屬性的指定,是用類似CSS語法的字串,例如用"rgba(255,255,255,0.5)"可以指定一個半透明的白色。如果不需要指定透明度,可以用"#FFFFFF"。一些特殊的屬性字串有規定可以指定的特性字串,例如lineCap只能指定"butt", "round", "square"其中之一,至於效果如何,試了就知道。

接下來用四個簡單例子來做測試吧。

繪圖
繪圖的部份,我就簡單地寫一個用1px的黑點為單位的畫圖測試,用到save(), restore(), beginPath(), closePath(), stroke(), moveTo(), lineTo()這幾個方法。

程式碼請見:http://gist.github.com/638699

在Canvas上面塗鴉的效果:

想要試試的話:http://www.fillano.idv.tw/canvastest001.html

變形
用scale(), rotate(), translate(), transform(), setTransform()等方法來做變形的效果。這些方法只對執行後到下一次執行其他變型的方法或restore()中間的繪圖有作用。每一次執行都會影響Canvas的trsansform matrix,然後繪圖的座標就被改變了。用一個簡單的例子來看會比較清楚:

程式碼請見:http://gist.github.com/638707

裡面的程式會每隔一秒執行繪圖動作,先畫一個叉叉,然後用一個正方形蓋過,然後用save()儲存環境,接著不斷旋轉畫布,然後再旋轉過的畫布上畫叉叉,最後執行restore(),在正方形外再畫一個方框。

畫出來的東西像這樣:

想要試試看的話:http://www.fillano.idv.tw/canvastest002.html

文字
文字的用法很簡單,去調整font屬性,然後用fillText(), strokeText()來畫出文字就可以。用textAlign調整左右對齊的方式,用textBaseline則可以調整文字在行中的位置。measureText()則可以量測一段字串繪出後的大小。

下面的例子很簡單,用一個text input 輸入文字,然後用點選按鈕後把文字畫到Canvas上:

程式碼請見:http://gist.github.com/638711

做出來的文字效果像這樣:

想要試試看的話:http://www.fillano.idv.tw/canvastest003.html

影像
時間剩下不多,快截稿了...

我這裡只結合drag & drop跟drawImage來把拖放的影像畫到Canvas上。

程式碼請見:http://gist.github.com/638718

把影像檔拖拉Canvas上頭,就會在Canvas上繪出:

想要試試看的話:http://www.fillano.idv.tw/canvastest004.html

其實更重要的是getImageData(), putImageData()以及createImageData(),他可以產生一個ImageData物件,裡面含有影像pixel的資料。有了這些資料,就可以做出多采多姿的應用。包括FPS遊戲。

參賽文章


上一篇
[HTML5試試看-9] canvas 與 2d context
下一篇
[HTML5試試看-11] canvas 與 3d context
系列文
HTML5試試看30
0
fillano
iT邦超人 1 級 ‧ 2010-10-22 00:11:15

再這樣live coding下去會死人...以後例子簡單一點比較好...Orz

0
wallace1987
iT邦新手 5 級 ‧ 2011-06-09 08:00:54

大大你好~
我現在剛接觸HTML5的技術~也沒有試過寫網頁的程式碼~
所以有很多不懂的地方~想請問大大~
現階段也只是試試書上以及網路的範例試著了解~
看到大大的範例~想請問大大~請問我要如何在自己的電腦上實做~
程式碼打完~但是丟到瀏覽器上~他卻不會跑~所以是哪個環節上出了問題~
希望大大可以幫我解答~

fillano iT邦超人 1 級 ‧ 2011-06-09 10:00:31 檢舉
  1. 請確認你的瀏覽器是否支援HTML5(ex. IE9, Firefox4, Chrome...)
  2. 如果直接透過瀏覽器跑,不使用伺服器會有問題的話,建議你試試看一些javascript code snippet服務,例如:http://jsfiddle.net/,你在上面貼好東西就可以預覽,做概念驗證還蠻方便的
fillano iT邦超人 1 級 ‧ 2011-06-09 10:04:58 檢舉

另外,HTML5規格還沒定案,目前其實每天都在修正,而且...瀏覽器實作部份的狀況恐怕也差不多。我自己在嘗試的時候,通常是直接看http://www.w3.org/TR/html5/文件來做參考,再看看在不同瀏覽器跑有什麼問題。剛開始看spec比較辛苦,不過這些東西看習慣以後,功力會大增的。

wordsmith iT邦高手 1 級 ‧ 2011-06-09 11:01:15 檢舉

只是試效果的話, chrome是最好的練習瀏覽器囉,常常就在背後更新了 XD

0
wallace1987
iT邦新手 5 級 ‧ 2011-06-18 15:48:49

大大你好~
我這幾天都在研究 canvas 的技巧 ~
我把知道的東西統合起來~
我自己做了一個自己想的範例~ 但是我又卡關了~
因為我想做到點到一個座標就會在那個座標出現圓的動畫~
但是小弟不才~ 只能做到點一次~ 在那個座標跑動畫~
如果我要做等動畫跑完~ 在點一次座標~ 又會再跑一次動畫~
還有我這個範例有個小小的BUG就是~在跑的時候~我滑鼠在點別的位置~動畫就會跑到那個位置~
請大大可以給點意見~ 我要怎麼做才好~
希望是可以做到~ 點一個座標就出現一個圓~ 然後我可以點很多個圓出來跑~

這是我的範例 請你過目
https://gist.github.com/9ab02a179bd3e087c241

0
fillano
iT邦超人 1 級 ‧ 2011-06-18 21:26:26

嗯...我想你的問題其實主要不是在canvas,而是Javascript上。建議你多瞭解Javascript的scope原理。我幫你改成這樣:
https://gist.github.com/1033092

跑起來像:

0
wallace1987
iT邦新手 5 級 ‧ 2011-06-18 23:32:57

請問大大 何謂scope原理 可以請大大講解一下嗎???
小弟的物件導向沒有很好 然後Javascript可能觀念有些錯誤
本來以為function中不能再寫function~ 沒想到可以~
我看完程式碼只理解到~我只要CLICK之後就會產生新的變數var startX以及var i ~
所以是否這樣的寫法~導致每個球都有獨自的變數var startX以及var i~
我當初不知道function中是否可以再寫function 所以都把他拆開來寫~
這裡可能就是我一直無法突破的地方~
然後又怕在一個function 宣告變數var startX~
其他function無法取得~(不知道我這想法是否正確請大大教導)
為何以大大的寫法看來同樣的變數var startX我只要按一次CLICK就可以一直重複宣告不互相影響~

請問 var startX = e.pageX-e.currentTarget.offsetTop;
這一行中的pageX currentTarget offsetTop 各是什麼意思???
跟滑鼠的馬克有關嗎~(對不起英文不好所以直接用中文打)

fillRect 中的座標所寫的東西 符號我也不太懂

然後為什麼在if(i>70)中寫ctx.save();她有何作用
globalCompositeOperation = "xor" 是什麼東東>///<
restore();是什麼
setTimeout()跟setInterval()有何不同
所以除非if(i>70)成立 不然 setTimeout() 一直做 是這意思嗎???
所以drawBall 裡面可以重覆呼叫自己:P

看到這我才發現我有好多地方想太多
可以請大大在這一點上 指導小弟嗎
小弟有把自己改過的程式碼又放上去了 目前是寫到可以一直畫一個了
本來是想用到陣列讓每一個都獨立跑 然後可以重複畫
沒想到大大的寫法更好根本不用陣列
小弟跟大大的想法到底有哪裡不同可以上請大大在這一點指導小弟嗎

感謝大大 花心思 教導小弟

小弟感激不盡

0
fillano
iT邦超人 1 級 ‧ 2011-06-19 10:53:30

嗚嗚,回了上萬字被系統拒絕然後清掉了...

關於Javascript與變數scope

如果想要精通Javascript,我通常會建議看O'reily的Javascript: the definitive guide以及Javascript: the good parts。

另外,建議用幾個關藉字組合上網查一下:Javascript, 變數(變量, variable), 作用範圍(作用域, scope), 閉包(closure),應該有不少好文章。

我在ithelp的鐵人賽寫過:Javascript面面觀:核心篇《變數範圍》,另外,更早以前因為研究ECMA-262規格,在自己的blog上寫過:搞清楚Javascript的變數作用範圍...寫的有點晦澀就是了XD

關於e.pageX等

我發現程式寫錯了Orz...我在gist上有修正,請參考。(offsetLeft與offsetTop應該相反)

e.pageX會取得click事件發生時,滑鼠游標相對於頁面左上角的水平距離(不是螢幕左上角,所以不會受到頁面捲動的影響)。e.currentTarget.offetLeft則可取得click事件發生時,所點選物件左上角相對於頁面左上角的水平距離。這兩個相減,就可以取得click事件發生時,滑鼠點選位置在所點選物件內的正確坐標,而且不受頁面捲動影響。e.pageY及e.currentTarget.offsetTop可以類推。

關於fillRect中坐標的寫法

我發現坐標小於0或是大於800時(超過canvas大小),會出現繪製問題,所以讓他不要超出。

耶,好像要超過字數限制了...

0
fillano
iT邦超人 1 級 ‧ 2011-06-19 11:08:15

續上

關於ctx.save()與ctx.restore()

請多參考標準文件:http://dev.w3.org/html5/2dcontext/Overview.html

canvas2d context中,有許多設定是全域的,為了方便管理,可以先用save()把這些東西一次存起來,在需要時用restore()回復。

globalCompositeOperation是用來控制圖形繪製到canvas上的方式(請參閱標準文件的解說),xor方便的地方,是在他會在第二次在同樣位置繪製同樣圖形時,把canvas上的圖形清掉。(我想你找一些圖學或是遊戲方面的書籍,就會講到這個)

關於setTimeout與setInterval

setInterval會重複執行,其實控制起來蠻麻煩的,所以除非有特定需求,你使用setTimeout就好了。這樣,你可以在需要重複執行時,才呼叫setTimeout。(而且你沒有把interval id存起來,這樣是沒辦法做clearInterval的)

結束。

fillano iT邦超人 1 級 ‧ 2011-06-19 11:10:17 檢舉

補充一下,解法不只一種,這只是用你程式修改的話,最快的做法。

0
fillano
iT邦超人 1 級 ‧ 2011-06-19 14:20:02

另外,要用陣列控制其實ok,我有一個想法,但是需要比較大幅修改流程。

  1. 每次click,會把參數物件加到陣列
  2. 會有一個render函數,用setInterval來每隔一段時間發動,每次發動都會把陣列中所有物件依序繪出,render函數每次畫完,會去調整參數物件中存放的參數,用來在下次繪圖時使用
  3. 當停止條件達成時,就從陣列中把參數物件移除

這樣就能改善繪圖時,圖形互相覆蓋的問題,效果應該更好。

0
fillano
iT邦超人 1 級 ‧ 2011-06-21 21:51:54

又改了一個版本,就是利用setInterval及render函數來做,還是在同一個gist裡面:
https://gist.github.com/1033092
(要看不同版本,可以點選右側Revisions下方的連結,改了三次,所以有三個)

如果想要做game,常常是利用這樣的方法來一次render,避免各自render造成畫面閃爍(可能會搭配一個隱藏的canvas)。間隔40 millisec左右畫一次動作會比較順暢(就是每秒24格),不過這樣會來不及點擊就...所以還是改成300。

0
html5
iT邦新手 5 級 ‧ 2012-05-25 00:44:59

請教您所撰寫的線上繪圖功能,因程式碼裡有指定一個jquery-1.4.2.min.js
請問可否有提供下載,因為想要在自己試著寫寫看,想參考您的程式碼
謝謝您的回覆喔!

fillano iT邦超人 1 級 ‧ 2012-05-25 09:19:25 檢舉

建議你把javascript網址改用CDN,例如:

&lt;pre class="c" name="code">
&lt;script type="text/javascript" src="http://code.jquery.com/jquery-1.4.2.min.js">&lt;/script>
html5 iT邦新手 5 級 ‧ 2012-05-27 23:22:52 檢舉

謝謝您的回答~

0
html5
iT邦新手 5 級 ‧ 2012-05-28 21:03:35

您好
因對HTML5不甚了解
您所提供之解決方法已測試過OK
但"結合drag & drop跟drawImage來把拖放的影像畫到Canvas上",這個範例我測不出來耶~

另再請教一事
是否CANVAS的互動功能皆須使用 src="http://code.jquery.com/jquery-1.4.2.min.js"
以上感謝大大的解惑,謝謝!

看更多先前的回應...收起先前的回應...
fillano iT邦超人 1 級 ‧ 2012-05-29 09:11:42 檢舉

不需要,使用jquery只是讓程式精簡一點。

fillano iT邦超人 1 級 ‧ 2012-05-29 09:15:43 檢舉

另外,drag & drop試不出來,表示您使用的瀏覽器不支援...您使用的瀏覽器及版本?

html5 iT邦新手 5 級 ‧ 2012-05-29 23:44:46 檢舉

了解,因為我不會jquery,所以還是要用javascript慢慢寫囉

我所使用的瀏覽器為Google Chrome v19.0.1084.52
麻煩您的解惑,謝謝

fillano iT邦超人 1 級 ‧ 2012-05-30 16:55:18 檢舉

Chrome應該可以才對...我在Chrome上面跑是OK的阿...有錯誤訊息嗎?(請打開開發者工具,看看Console有跑出什麼錯誤訊息)

html5 iT邦新手 5 級 ‧ 2012-05-30 21:11:37 檢舉

您好
沒有錯誤訊息耶
但就是無法執行????
請問測試 HTML5 的API功能,需要建置甚麼樣的環境嗎
我是用Dreamweaver5.5來撰寫HTML5的語法及測試呢
我只用本機網站資料夾來設定及測試,會是這個原因嗎??
實在是又要麻煩您的解答了,謝謝您~

fillano iT邦超人 1 級 ‧ 2012-05-31 19:31:52 檢舉

有用文章中的連結來試嗎?如果是你自己寫的,請貼一下程式碼做參考。

html5 iT邦新手 5 級 ‧ 2012-05-31 20:35:28 檢舉

我是複製大大所寫的程式碼來測試的

我要留言

立即登入留言