嗨啰!大家好!这篇文章是这个系列的最后了,来说说如何用滑鼠和canvas内的绘图做简单的互动吧!
...这次的前言是不是有点少XD,但是前言真的真的真的很难想啊!这篇先简单进入正文吧!
首先,今天的主题是要使用滑鼠和canvas做动画,所以我们必须先取得滑鼠在画面上的座标才行,这时候可以使用JavaScript的addEventListener
监听器,来监听mousemove
滑鼠移动的过程来触发事件,透过事件可以取得滑鼠目前在画面上的x
和y
座标位置,说起来很简单,做起来其实也不难,我们直接试试吧!
//使用addEventListener监听器,监听mousemove滑鼠移动,并触发后面的functionwindow.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;})//一个绘製的functionconst 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;})//一个绘製的functionconst 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();}//绘製光芒线条的functionconst 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
中继续绘製其他线条:
//绘製光芒线条的functionconst 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
中,另外我把绘製十字光芒射线的部分改成用迴圈处理:
//绘製光芒线条的functionconst 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好了,另外再加上其他三条斜线:
//绘製光芒线条的functionconst 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,让很少熬夜的我大爆气,不过很快就接受现实继续打完了,弄到现在感觉可以去麦当劳吃个满福堡在睡觉,哈哈哈。
那最后如果对以上文章有任何问题或是哪些地方搞错了,都可以在留言告诉我,或是各位大大可以留下各自的经验或想法可以互相讨论的都很欢迎,谢谢各位大大的观看。