Hello!大家好!不知道大家有沒有喜歡的動畫,可能是海賊王、七龍珠、火影忍者、灌籃高手...等等,說到小弟我啊,最喜歡的非棋靈王莫屬了!但是我們今天不是要來聊動畫的,是要來講動畫!看了那麼久的動畫,記得以前還以為是電視機裡面有人,在裡面演給我看,不過一直到了國中,才慢慢結束了這美好的幻想XD,所謂動畫啊,原理其實很簡單,就是由一張張些微不同的圖片快速翻閱,讓我們的眼睛藉由視覺殘留的效果,感覺圖片好像真的動起來一樣!
上一段說了那麼多!就是為了接正文了!讓我們把動畫的原理套來網頁,記得我們上一篇已經可以用canvas畫出靜態的畫面了嗎?那接著我們讓他重複繪畫,並且在每次繪畫時都偷偷改變一些設定值試試看,不過首先我們還是先需要一張圖,當做複習,就來畫個台灣吧XD
讓我們來看看Google地圖上的台灣,由於小弟我覺得難度有點稍高,所以小改造一些XD::
好的!那就像上面一樣,我畫紅線把台灣大致上的輪廓用直線處理掉,那我們需要16個點,先來處理吧!我們把起手式打出來如下:
HTML
<canvas id="myCanvas"></canvas>
JavaScript
let canvas = document.getElementById("myCanvas");
let ctx = canvas.getContext("2d");
canvas.height = window.innerHeight;
canvas.width = window.innerWidth;
然後...在JavaScript中畫出相對應的16個點之後相連,會變成以下的樣子,感覺我的台灣有點矮肥XD:
ctx.beginPath();
ctx.moveTo(200,50)
ctx.lineTo(180,55)
ctx.lineTo(165,80)
ctx.lineTo(130,100)
ctx.lineTo(70,250)
ctx.lineTo(60,300)
ctx.lineTo(80,350)
ctx.lineTo(120,380)
ctx.lineTo(140,420)
ctx.lineTo(160,425)
ctx.lineTo(160,385)
ctx.lineTo(175,330)
ctx.lineTo(220,290)
ctx.lineTo(240,200)
ctx.lineTo(260,150)
ctx.lineTo(250,110)
ctx.lineTo(270,90)
ctx.lineTo(250,75)
ctx.lineTo(215,65)
ctx.closePath()
ctx.stroke()
好的,以上就是上一次課堂上所說的部分,那我們該如何讓他做變化呢,其實很簡單,既然需要多張靜態圖片,那我們就讓他一直重複繪製靜態圖片!首先我把以上繪製台灣的線條變成一個function
,就叫draw
好了,我們試著讓每一次執行前都用clearRect()
把原本的圖案清掉,並且搭配setInterval
重複執行draw
,讓他會在網頁開著的期間一直重新繪製台灣,所以我們把JavaScript改成以下的樣子:
PS一下,我順便把,draw
內的lineTo(x,y)
整理一下,把所有的座標都放進一個陣列中,接著再跑迴圈去畫點的位置,這樣讓程式碼會比較乾淨一點。
let canvas = document.getElementById("myCanvas");
let ctx = canvas.getContext("2d");
canvas.height = window.innerHeight;
canvas.width = window.innerWidth;
//把台灣的x,y座標整理到個陣列中
let arrTaiwan =[[200,50],[180,55],[165,80],[130,100],[70,250],[60,300],[80,350],[120,380],[140,420],[160,425],[160,385],[175,330],[220,290],[240,200],[260,150],[250,110],[270,90],[250,75],[215,65]]
//執行初始繪製
draw()
//繪製台灣
function draw(){
/*清掉canvas內的元素
第1,2個參數是x和y座標,指說從哪裡開始清掉繪製的圖形,
這邊從(0,0)開始也就是網頁的最左上角,
第3,4個參數是清掉的範圍,因為範圍是矩形,所以指定寬和高,
這邊的寬高指定為canvas的寬高。*/
ctx.clearRect(0, 0, canvas.width, canvas.height)
ctx.beginPath();
ctx.moveTo(200,50)
//使用迴圈去讀陣列中的內容畫線
for(let i=1;i<=arrTaiwan.length-1;i++){
ctx.lineTo(arrTaiwan[i][0],arrTaiwan[i][1])
}
ctx.closePath()
ctx.stroke()
}
//再最後重複執行繪製,讓他50毫秒重新繪製一次
setInterval('draw()',50)
但是目前我們還是看不出他有重新繪製的感覺,因為他每一次繪製都是同一個樣子,所以我們來微調他每次重新繪製的一些屬性,先在function
的外面建立一個全域變數time
,並在draw
的最後去增加他的值,代表重新繪製的次數,那要怎麼看到他的變化呢?我們使用最簡單ineWidth
改變線條寬度來測試看看吧!
let canvas = document.getElementById("myCanvas");
let ctx = canvas.getContext("2d");
canvas.height = window.innerHeight;
canvas.width = window.innerWidth;
//設定全域變數itme
let time = 0;
let arrTaiwan =[[200,50],[180,55],[165,80],[130,100],[70,250],[60,300],[80,350],[120,380],[140,420],[160,425],[160,385],[175,330],[220,290],[240,200],[260,150],[250,110],[270,90],[250,75],[215,65]]
draw()
function draw(){
ctx.clearRect(0, 0, canvas.width, canvas.height)
//設定寬度為time,所以每一次繪製都會改變寬度
ctx.lineWidth = time
ctx.beginPath();
ctx.moveTo(200,50)
for(let i=1;i<=arrTaiwan.length-1;i++){
ctx.lineTo(arrTaiwan[i][0],arrTaiwan[i][1])
}
ctx.closePath()
ctx.stroke()
//設定itme跑到10的時候歸0不然他會一直加深到永無止盡
if (time == 10){
time = 0
}
//結束時累加time的值
time++
}
//再最後重複執行繪製,讓他50毫秒重新繪製一次
setInterval('draw()',50)
如此一來應該可以看見畫面上的台灣,不斷的加深線條寬度後又變細,無限循環,這邊我就不再另外貼圖了,因為也看不出他動的效果,不過如果看到相同的畫面代表大家已經完成了最簡單的動畫了!
接著要與滑鼠做互動動畫有兩種方式,第一種是用addEventListener
監聽器,讓滑鼠每次移動時都會觸發事件,把座標記錄下來,第二種是使用onmousemove
配合canvas的isPointInPath()
,這個方法放在每次繪製前,去判斷滑鼠的游標有沒有在剛剛繪製的圖形內,如果有的話就去改變圖形樣式。這篇文章的最後我們先來說說isPointInPath()
,下一次再好好詳述addEventListener
的處理方式。
那現在我們把onmousemove
放到draw
前,再另外使用isPointInPath()
判斷目前滑鼠座標是否在剛剛繪製的路徑中,如果有的話我們就讓繪製區域變色!現在直接看程式碼吧!
let canvas = document.getElementById("myCanvas");
let ctx = canvas.getContext("2d");
canvas.height = window.innerHeight;
canvas.width = window.innerWidth;
let time = 0
//這邊再新增一個全域變數fillColor,
//用來設定填滿圖形的色彩,預設為白色
let fillColor = "#FFFFFF"
let arrTaiwan =[[200,50],[180,55],[165,80],[130,100],[70,250],[60,300],[80,350],[120,380],[140,420],[160,425],[160,385],[175,330],[220,290],[240,200],[260,150],[250,110],[270,90],[250,75],[215,65]]
//執行初始繪製
draw()
//繪製台灣
function draw(){
ctx.clearRect(0, 0, canvas.width, canvas.height)
ctx.fillStyle = fillColor
ctx.lineWidth = time
ctx.beginPath();
ctx.moveTo(200,50)
for(let i=1;i<=arrTaiwan.length-1;i++){
ctx.lineTo(arrTaiwan[i][0],arrTaiwan[i][1])
}
ctx.closePath()
ctx.stroke()
ctx.fill()
if (time == 10){
time = 0
}
//結束時累加time的值
time++
}
//使用onmousemove事件去抓取滑鼠是否移動到canvas中
//如果有的話就觸發事件
canvas.onmousemove = function (e) {
//先獲取目前游標在的座標
var x = e.pageX, y =e.pageY;
//使用isPointInPath()判斷滑鼠座標是否在上一次繪製圖形的path之內,
//傳入參數為我們獲取的滑鼠x,y座標
if(ctx.isPointInPath(x,y)) {
//如果在繪製圖形內的話設定填滿顏色為紅色
fillColor="#FF0000";
}
else{
//如果在繪製圖形外的話設定填滿顏色為白色
fillColor="#FFFFFF";
}
}
//接收我們剛剛的設定後繪製新的圖形
setInterval('draw()',50)
登愣!這麼一來,不只粗細會不斷變動,只要滑鼠移到台灣的範圍內,就會變成以紅色填滿,如果移開就會變回白色,就像下面的圖一樣,右邊是一般狀態,但滑鼠只要進入台灣的範圍,就會變成紅色的!話說剛剛在截圖的時候,還以為自己畫的是神奇寶貝的鐵甲蛹...
所以只要以上面這種方式,就可以簡單地做出互動式的網頁動畫了!當然因為前端的世界真的很變化多端,所以一定會有更好的處理方式來做到同樣的效果,如果各位大大有任何想法都可以提出,我都會找時間去研究看看,像第一篇的留言真的出現很多高手XD,也很感謝他們做分享,如果以上文章中有任何問題或錯誤也都可以留言告訴我,我會盡快改進,那接著下一篇再把這些原理做更進一步地運用,繼續做出各種有趣的頁面吧!
另外這禮拜六日可能就不會有文章了,因為小弟我禮拜五開始就要上台北放風了,所以讓我偷懶一個禮拜吧!哈哈哈!我們下下禮拜見,謝謝大家
好有趣,原來 canvas 的互動事件是這樣做出來的,
還有沒想到你真的把台灣畫出來了。
哈哈哈,對啊!我那時候學到也有「哇!」的驚喜感!
雖然畫出台灣,但是天賦樹就又是另外一回事了
isPointInPath()
是好物阿...不然判斷多邊形的intersection有點麻煩(凸多邊形比較簡單,如果有地方是凹的...)
真的!我第一次學的時候只知道用addEventListener
去取滑鼠座標,是剛好讓我爬文爬到這個function,算是救了我一命吧