[笔记][HTML][JavaScript]canvas的基本用法(3)-最后用滑鼠互动动画做个结尾!

嗨啰!大家好!这篇文章是这个系列的最后了,来说说如何用滑鼠和canvas内的绘图做简单的互动吧!

...这次的前言是不是有点少XD,但是前言真的真的真的很难想啊!这篇先简单进入正文吧!

首先,今天的主题是要使用滑鼠和canvas做动画,所以我们必须先取得滑鼠在画面上的座标才行,这时候可以使用JavaScript的addEventListener监听器,来监听mousemove滑鼠移动的过程来触发事件,透过事件可以取得滑鼠目前在画面上的xy座标位置,说起来很简单,做起来其实也不难,我们直接试试吧!

//使用addEventListener监听器,监听mousemove滑鼠移动,并触发后面的functionwindow.addEventListener('mousemove',(event) => {  /*在function内会传入我们监听的滑鼠物件,    我们可以从这个物件中取得我们要的资料:    x座标 event.pageX 及y座标 event.pageY    并把它印在console中*/  console.log(`${event.pageX},${event.pageY}`)})

这时候打开console.log就会看见每一次滑鼠移动时的座标都会被记录在console上。
http://img2.58codes.com/2024/20106935uoV3S37qvI.jpg
接着把上面那段取得的滑鼠座标,加进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物件中的座标当圆心重新绘製一次圆,所以在画面上的感觉就像圆会跟着滑鼠跑:
http://img2.58codes.com/2024/20106935cp3dxC1ioC.png
之后我们建立一个新的funcitonlightLine来绘製太阳周围的光芒线条,可以看看下图:
http://img2.58codes.com/2024/20106935hC182f4qL9.png
黑色线条为太阳的半径,绿色线条为空白的地方,因为线条通常不会黏在太阳的圆上,而橘线的长度就是我们光芒线条的长度了,所以依照图解,从圆心开始把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)

加进去后可以看见原本的太阳的右边多了一条横线:
http://img2.58codes.com/2024/20106935eR372D6eh1.png
另外左边的横线也可以用相同的方式,只是往右边的距离是用加的,所以要绘製左横线,就变成要把距离扣掉。上纵线的话得把原本变动的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();}

都加完后太阳就会拥有十字的光芒线条了:
http://img2.58codes.com/2024/20106935odPx0BJwWX.png
不过这样子看起来还是有点单调,所以接着要绘製斜线的部分,这里我借我PS一下,其实接下来的部分用一种「极座标」的方式处理会比较好,但是如果在这里说明极座标的话,我就觉得不太基本了XD,所以本篇我会先用一些简单的方式来处理斜线的部分,关于极座标我会再另外写文章来说明。

那绘製斜线前一样先上图,应该会比较好理解吧?(我真的尽力画好了XD):
http://img2.58codes.com/2024/20106935DJ66ylJXlT.jpg
因为接下来要画的是斜线,所以需要同时改变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();}

来看看加上斜线的太阳会长什么样子吧!
http://img2.58codes.com/2024/20106935zgCfaDBcf0.png
感觉斜线的距离有点短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,来看看完整版的太阳长什么样子吧!
http://img2.58codes.com/2024/20106935ndl07Cye04.png
最后来模拟一下天色的变化吧!首先要先预设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();}

先绘製一个蓝色的方形,再绘製太阳及线条,因为顺序的关係,方形在最下面就成了背景颜色,设定好后会如下图:
http://img2.58codes.com/2024/20106935VlkSu6kBqY.png
这边因为有了背景颜色,所以可能会注意到一个之前没有发现的问题,就是一开始设定canvas的宽高的这两行:

canvas.height = window.innerHeight;canvas.width = window.innerWidth;

他会在新视窗打开时去取目前视窗的宽高,并把这个数值设定给canvas,所以如果我一开始的视窗是小的,运行时再把它给拉大,就会出现以下的情形:
http://img2.58codes.com/2024/20106935M3aHeDxDvR.png
canvas的宽高还是维持在一开始的长度,不会随着视窗大小改变,超过那个距离就不会绘製图形,要解决这个问题也很简单,应该很多大大在发现这个问题时就马上解决了!我们只需要把以上两行放进draw中,让他每一次绘製前都重新去抓目前的视窗大小,重新给canvas,再开始绘製,就搞定这个问题了!

那为什么我要设定背景颜色呢?本篇文章的最后,我要让背景颜色随着太阳的高低有所变化,就像天色一样,太阳下山就暗掉了!眼尖的大大应该也有发觉,我在设定背景颜色的时候是使用rgb,这可不是因为心血来潮哦,而是因为使用grb就可以更轻易的去改变些微的颜色,首先把会变动数值的greenblue都设成变数:

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();}

终于完成的现在,来感受一下成果吧!
http://img2.58codes.com/2024/20106935Py4qCeY88f.jpg
附上这篇文章的codePen连结

以上就是这个系列的最终回了,这篇文章打了很久很久,中途还因为突发事件重打了一次XD,让很少熬夜的我大爆气,不过很快就接受现实继续打完了,弄到现在感觉可以去麦当劳吃个满福堡在睡觉,哈哈哈。

那最后如果对以上文章有任何问题或是哪些地方搞错了,都可以在留言告诉我,或是各位大大可以留下各自的经验或想法可以互相讨论的都很欢迎,谢谢各位大大的观看http://img2.58codes.com/2024/emoticon41.gif


关于作者: 网站小编

码农网专注IT技术教程资源分享平台,学习资源下载网站,58码农网包含计算机技术、网站程序源码下载、编程技术论坛、互联网资源下载等产品服务,提供原创、优质、完整内容的专业码农交流分享平台。

热门文章