嗨囉!大家好!這篇文章是這個系列的最後了,來說說如何用滑鼠和canvas內的繪圖做簡單的互動吧!
...這次的前言是不是有點少XD,但是前言真的真的真的很難想啊!這篇先簡單進入正文吧!
首先,今天的主題是要使用滑鼠和canvas做動畫,所以我們必須先取得滑鼠在畫面上的座標才行,這時候可以使用JavaScript的addEventListener
監聽器,來監聽mousemove
滑鼠移動的過程來觸發事件,透過事件可以取得滑鼠目前在畫面上的x
和y
座標位置,說起來很簡單,做起來其實也不難,我們直接試試吧!
//使用addEventListener監聽器,監聽mousemove滑鼠移動,並觸發後面的function
window.addEventListener('mousemove',(event) => {
/*在function內會傳入我們監聽的滑鼠物件,
我們可以從這個物件中取得我們要的資料:
x座標 event.pageX 及y座標 event.pageY
並把它印在console中*/
console.log(`${event.pageX},${event.pageY}`)
})
這時候打開console.log就會看見每一次滑鼠移動時的座標都會被記錄在console上。
接著把上面那段取得的滑鼠座標,加進canvas的起手式中:
HTML
<canvas id="myCanvas"></canvas>
JavaScript
let canvas = document.getElementById("myCanvas");
let ctx = canvas.getContext("2d");
canvas.height = window.innerHeight;
canvas.width = window.innerWidth;
//建立一個物件儲存滑鼠目前的x,y座標
let mouse = {
x : 0,
y : 0,
}
//加入監聽器
window.addEventListener('mousemove',(event) => {
//在這裡把滑鼠座標寫到物件mouse中
mouse.x = event.pageX;
mouse.y = event.pageY;
})
那取了滑鼠座標後可以做什麼呢?來以這個座標當圓心畫個太陽吧!
let canvas = document.getElementById("myCanvas");
let ctx = canvas.getContext("2d");
canvas.height = window.innerHeight;
canvas.width = window.innerWidth;
let mouse = {
x : 0,
y : 0,
}
window.addEventListener('mousemove',(event) => {
mouse.x = event.pageX;
mouse.y = event.pageY;
})
//一個繪製的function
const draw = () =>{
//先清掉cvanvas目前繪製的圖形
ctx.clearRect(0, 0, canvas.width, canvas.height);
//開始作畫
ctx.beginPath();
//以滑鼠座標為圓心,畫一個半徑為30的圓形
ctx.arc(mouse.x,mouse.y,30,0,Math.PI*2);
//用橘色畫線
ctx.strokeStyle="#FF5511";
ctx.stroke();
//黃色填滿
ctx.fillStyle="#FFFF00";
ctx.fill();
}
//在最後重複執行繪製,讓他50毫秒繪製一次
setInterval('draw()',50)
如此一來,畫面上應該會出現追著滑鼠跑的圓形(原諒我截不到鼠標...),因為滑鼠只要移動,新的座標就會一直被addEventListener
抓到,並寫進mouse物件中,而我們每次繪製都會先清掉之前的圓,再去使用mouse物件中的座標當圓心重新繪製一次圓,所以在畫面上的感覺就像圓會跟著滑鼠跑:
之後我們建立一個新的funcitonlightLine
來繪製太陽周圍的光芒線條,可以看看下圖:
黑色線條為太陽的半徑,綠色線條為空白的地方,因為線條通常不會黏在太陽的圓上,而橘線的長度就是我們光芒線條的長度了,所以依照圖解,從圓心開始把x座標+加上黑色線條的30,再加上綠色線條的長度10,而y軸的高度不變,我們可以得到橘色線條的起始點,而他的終點只需要再起始點的x軸座標再加上20,就可以畫出右邊的橫線了,把以上的解釋寫成程式碼:
const lightLine = () =>{
//設定線條顏色為橘色
ctx.strokeStyle="#FF5511";
//開始繪圖
ctx.beginPath();
//起始點為滑鼠的x座標加上黑色線條的30及綠色線條的10
ctx.moveTo(mouse.x+(30+10),mouse.y);
//終點為x座標再加上20的距離
ctx.lineTo(mouse.x+((30+10)+20),mouse.y);
//畫線
ctx.stroke();
}
之後把lightLine
加到原本的程式中:
let canvas = document.getElementById("myCanvas");
let ctx = canvas.getContext("2d");
canvas.height = window.innerHeight;
canvas.width = window.innerWidth;
let mouse = {
x : 0,
y : 0,
}
window.addEventListener('mousemove',(event) => {
mouse.x = event.pageX;
mouse.y = event.pageY;
})
//一個繪製的function
const draw = () =>{
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.beginPath();
ctx.arc(mouse.x,mouse.y,30,0,Math.PI*2);
ctx.strokeStyle="#FF5511";
ctx.stroke();
ctx.fillStyle="#FFFF00";
ctx.fill();
//繪製光芒線條
lightLine();
}
//繪製光芒線條的function
const lightLine = () =>{
//設定線條顏色
ctx.strokeStyle="#FF5511";
//右橫線
ctx.beginPath();
ctx.moveTo(mouse.x+(30+10),mouse.y);
ctx.lineTo(mouse.x+((30+10)+20),mouse.y);
ctx.stroke();
}
setInterval('draw()',50)
加進去後可以看見原本的太陽的右邊多了一條橫線:
另外左邊的橫線也可以用相同的方式,只是往右邊的距離是用加的,所以要繪製左橫線,就變成要把距離扣掉。上縱線的話得把原本變動的x座標變成y座標,y從上方開始越往下越大,所以上縱線必須用扣的,而下縱線則是用加的。知道原理後就可以在lightLine
中繼續繪製其他線條:
//繪製光芒線條的function
const lightLine = () =>{
//設定線條顏色
ctx.strokeStyle="#FF5511";
//右橫線
ctx.beginPath();
ctx.moveTo(mouse.x+(30+10),mouse.y);
ctx.lineTo(mouse.x+((30+10)+20),mouse.y);
ctx.stroke();
//左橫線
ctx.beginPath();
ctx.moveTo(mouse.x-(30+10),mouse.y);
ctx.lineTo(mouse.x-((30+10)+20),mouse.y);
ctx.stroke();
//上縱線
ctx.beginPath();
ctx.moveTo(mouse.x,mouse.y-(30+10));
ctx.lineTo(mouse.x,mouse.y-((30+10)+20));
ctx.stroke();
//下縱線
ctx.beginPath();
ctx.moveTo(mouse.x,mouse.y+(30+10));
ctx.lineTo(mouse.x,mouse.y+((30+10)+20));
ctx.stroke();
}
都加完後太陽就會擁有十字的光芒線條了:
不過這樣子看起來還是有點單調,所以接著要繪製斜線的部分,這裡我借我PS一下,其實接下來的部分用一種「極座標」的方式處理會比較好,但是如果在這裡說明極座標的話,我就覺得不太基本了XD,所以本篇我會先用一些簡單的方式來處理斜線的部分,關於極座標我會再另外寫文章來說明。
那繪製斜線前一樣先上圖,應該會比較好理解吧?(我真的盡力畫好了XD):
因為接下來要畫的是斜線,所以需要同時改變x及y軸座標,如上圖我們從圓心點的位置,讓y軸延著黑線向上加上30,再沿著藍線的部分把x軸的座標加上30,這時候的座標會在綠色線和橘色線的交接處,剛好在橘色線條的起始點上。
第二部分要取的是橘色線條的終點,如果有理解上面那段的話,就可以知道,x及y軸同時改變的距離越長,那最後的位置也會離圓心越遠。所以把x和y同時加上40,也就是黑色線條加上紫色線條的距離,最後所在的地方就是橘色線條的終點。
把上面兩段寫進lightLine
中,另外我把繪製十字光芒射線的部分改成用迴圈處理:
//繪製光芒線條的function
const lightLine = () =>{
//設定線條顏色
ctx.strokeStyle="#FF5511";
//分別是起始點和終點需要增加的距離
let moveLength = 40
let lineLength = 60
//第一次迴圈加上距離,第二次減掉距離
for(let i=-1;i<=1;i+=2){
//橫線
ctx.beginPath();
ctx.moveTo(mouse.x+(moveLength*i),mouse.y);
ctx.lineTo(mouse.x+(lineLength*i),mouse.y);
ctx.stroke();
//縱線
ctx.beginPath();
ctx.moveTo(mouse.x,mouse.y+(moveLength*i));
ctx.lineTo(mouse.x,mouse.y+(lineLength*i));
ctx.stroke();
}
/*因為x和y都是變多,
所以x軸往右,y軸往下,
會繪製出下右斜線*/
ctx.beginPath();
//把x軸加上藍色線條,y軸加上黑色線條,長度都是30
ctx.moveTo(mouse.x+30,mouse.y+30);
//把x和y軸座標加上黑色線條30和紫色線條的長度10
ctx.lineTo(mouse.x+(30+10),mouse.y+(30+10));
ctx.stroke();
}
來看看加上斜線的太陽會長什麼樣子吧!
感覺斜線的距離有點短XD,下一段把紫色距離的10變成15好了,另外再加上其他三條斜線:
//繪製光芒線條的function
const lightLine = () =>{
//設定線條顏色
ctx.strokeStyle="#FF5511";
//分別是起始點和終點需要增加的距離
let moveLength = 40
let lineLength = 60
//第一次迴圈加上距離,第二次減掉距離
for(let i=-1;i<=1;i+=2){
//橫線
ctx.beginPath();
ctx.moveTo(mouse.x+(moveLength*i),mouse.y);
ctx.lineTo(mouse.x+(lineLength*i),mouse.y);
ctx.stroke();
//縱線
ctx.beginPath();
ctx.moveTo(mouse.x,mouse.y+(moveLength*i));
ctx.lineTo(mouse.x,mouse.y+(lineLength*i));
ctx.stroke();
}
//下右斜線
ctx.beginPath();
ctx.moveTo(mouse.x+30,mouse.y+30);
ctx.lineTo(mouse.x+(30+15),mouse.y+(30+15));
ctx.stroke();
//下左斜線
ctx.beginPath();
ctx.moveTo(mouse.x-30,mouse.y+30);
ctx.lineTo(mouse.x-(30+15),mouse.y+(30+15));
ctx.stroke();
//上右斜線
ctx.beginPath();
ctx.moveTo(mouse.x+30,mouse.y-30);
ctx.lineTo(mouse.x+(30+15),mouse.y-(30+15));
ctx.stroke();
//上左斜線
ctx.beginPath();
ctx.moveTo(mouse.x-30,mouse.y-30);
ctx.lineTo(mouse.x-(30+15),mouse.y-(30+15));
ctx.stroke();
}
完成lightLine
後就可以不用管他了XD,來看看完整版的太陽長什麼樣子吧!
最後來模擬一下天色的變化吧!首先要先預設canvas的背景顏色,這部分我們在draw
中設置:
const draw = () =>{
ctx.clearRect(0, 0, canvas.width, canvas.height);
//設定背景顏色
//用rgb的方式把填滿的色彩設為天空藍
ctx.fillStyle = 'rgb(0, 180, 255)';
//使用fillRect繪製一個和canvas一樣寬高的方形
ctx.fillRect(0, 0, canvas.width, canvas.height);
//之後繪製太陽
ctx.beginPath();
ctx.arc(mouse.x,mouse.y,30,0,Math.PI*2);
ctx.strokeStyle="#FF5511";
ctx.stroke();
ctx.fillStyle="#FFFF00";
ctx.fill();
//繪製光芒線條
lightLine();
}
先繪製一個藍色的方形,再繪製太陽及線條,因為順序的關係,方形在最下面就成了背景顏色,設定好後會如下圖:
這邊因為有了背景顏色,所以可能會注意到一個之前沒有發現的問題,就是一開始設定canvas的寬高的這兩行:
canvas.height = window.innerHeight;
canvas.width = window.innerWidth;
他會在新視窗打開時去取目前視窗的寬高,並把這個數值設定給canvas,所以如果我一開始的視窗是小的,運行時再把它給拉大,就會出現以下的情形:
canvas的寬高還是維持在一開始的長度,不會隨著視窗大小改變,超過那個距離就不會繪製圖形,要解決這個問題也很簡單,應該很多大大在發現這個問題時就馬上解決了!我們只需要把以上兩行放進draw
中,讓他每一次繪製前都重新去抓目前的視窗大小,重新給canvas,再開始繪製,就搞定這個問題了!
那為什麼我要設定背景顏色呢?本篇文章的最後,我要讓背景顏色隨著太陽的高低有所變化,就像天色一樣,太陽下山就暗掉了!眼尖的大大應該也有發覺,我在設定背景顏色的時候是使用rgb,這可不是因為心血來潮哦,而是因為使用grb就可以更輕易的去改變些微的顏色,首先把會變動數值的green
和blue
都設成變數:
const draw = () =>{
//把視窗大小重新設定給canvas
canvas.height = window.innerHeight;
canvas.width = window.innerWidth;
ctx.clearRect(0, 0, canvas.width, canvas.height);
//設定背景顏色
let green = 180;
let blue = 255;
ctx.fillStyle = `rgb(0, ${green}, ${blue})`;
ctx.fillRect(0, 0, canvas.width, canvas.height);
//之後繪製太陽
ctx.beginPath();
ctx.arc(mouse.x,mouse.y,30,0,Math.PI*2);
ctx.strokeStyle="#FF5511";
ctx.stroke();
ctx.fillStyle="#FFFF00";
ctx.fill();
//繪製光芒線條
lightLine();
}
這裡最簡單的方法就是將green和blue直接減掉滑鼠目前在的y座標,因為最上方的y是0,所以滑鼠如果視窗下方移動,就等於太陽往下方移動,y就會越來越大,而green和blue扣掉越來越大的y後就會越來越小,直到變成代表黑色的rgb(0, 0, 0)
。
但是這樣會有個問題,由於我們的green及blue最大也只有255而已,所以其實y座標只要超過255,背景就會變成黑色。不會再有變化了,我們必須想個方法讓他等比例的被扣掉數值。
先處理green,他的基本數值是180,這裡先把180除上目前視窗的高度,會得到每一點高度等於180中的多少比例,當我們得到這個比例後,再將他乘上目前滑鼠的y軸高度,就會是green最後要扣掉的數值了。
藍色的部分因為初始值比較大,是255,所以當綠色的180被扣掉1的時候,藍色的255也許會被扣掉1.5左右,這麼一來滑鼠的y軸到一個高度時,天空會慢慢變成綠色,這並不是我想要的天色,所以這裡我一樣把藍色的下降比例設成180和green一樣,這樣藍色的比例就會一直高於綠色。
不過以上的比例是我自己是出來認為最適合的,各位大大也可以試著調整不同的數值觀察變化,來看看最後draw
的樣子:
const draw = () =>{
//把視窗大小重新設定給canvas
canvas.height = window.innerHeight;
canvas.width = window.innerWidth;
ctx.clearRect(0, 0, canvas.width, canvas.height);
//設定背景顏色
//先用180除上目前視窗的高度,再乘上目前滑鼠的y軸高度
let green = 180-(mouse.y*(180/window.innerHeight));
/*藍色部分沒有用255除上目前視窗高度是因為
如果使用255那藍色被扣掉的幅度會比綠色被扣掉的幅度還大,
所以這裡我選擇設定和綠色相同的比例,而最後y到視窗最下方時,
也會顯示很深很深的藍色,而不是黑色*/
let blue = 255-(mouse.y*(180/window.innerHeight));
ctx.fillStyle = `rgb(0, ${green}, ${blue})`;
ctx.fillRect(0, 0, canvas.width, canvas.height);
//之後繪製太陽
ctx.beginPath();
ctx.arc(mouse.x,mouse.y,30,0,Math.PI*2);
ctx.strokeStyle="#FF5511";
ctx.stroke();
ctx.fillStyle="#FFFF00";
ctx.fill();
//繪製光芒線條
lightLine();
}
終於完成的現在,來感受一下成果吧!
附上這篇文章的codePen連結
以上就是這個系列的最終回了,這篇文章打了很久很久,中途還因為突發事件重打了一次XD,讓很少熬夜的我大爆氣,不過很快就接受現實繼續打完了,弄到現在感覺可以去麥當勞吃個滿福堡在睡覺,哈哈哈。
那最後如果對以上文章有任何問題或是哪些地方搞錯了,都可以在留言告訴我,或是各位大大可以留下各自的經驗或想法可以互相討論的都很歡迎,謝謝各位大大的觀看。
網頁滑鼠滑了滑,玩了玩,酷
讓畫面動起來真的是一件很酷的事情!
不過真的是需要非常的有耐心XD
話說好久沒有看到大大發文了
最近在忙處理公司的事情
哈哈,大大辛苦了!
期待你有空在分享知識!
好有趣喔,顏色的變換原來可以這樣設計。
對啊,其實只要知道一些基本的原理就可以做出很多東西了XD
不過下一篇的極座標讓以前沒學好三角函數的我頭很痛
哈哈哈,最近為了解 Code Jam 第四題也在看三角函數,期待下一篇極座標文章!!
說到重打,大家都有過的痛,哈哈哈,
推薦一下我之前寫的替代方案,現在都先打在 VSCode 然後順便推 git 做版控。
[VSCode] Visual Studio Code 寫 Markdown 使用 iT邦樣式
不過可能最新版的 VSCode 會有問題,自從更新某一版壞掉後,就再也不敢更新了。
我現在還卡在第三題,要讓他跑完3乘3,再進行下一個3乘3的土地,
一樣理解題目就花了非常久XD
沒想到VSCode有這種套件可以用!
這禮拜也來試試看,有問題再詢問您,
我的VSCode下載下來後,從來沒更新過XD,
所以每次開起來都再右上角不斷通知我,哈哈哈。
那和我一樣,繼續不要更新。