持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第2天,点击查看活动详情
烟花秀
从2010年起,湖南省长沙市委、市政府提出了推进城市国际化、把长沙打造成具有国际影响力的文化名城的目标,在长沙橘子洲头每周六晚上举办一场烟花秀,当初有幸到现场观看过一次,终身难忘。本想这次国庆节再去观摩一场,无奈疫情反反复复,为了不给国家和人民添乱,就只能在家通过代码的形式再来看一次了,虽然没有现场那么绚丽多彩,但胜在纯手工打造。接下来就陪着老婆一起来看这场烟花秀吧!
实现思路
要实现这么一个绚丽烟花的效果,需要的前置知识很多,包括了三角函数、js
中的二进制运算等,这些听起来很虚,大家不要慌,接下来我们就一条一条抽丝拨茧,让大家彻底学会这个烟花效果。
因为这个效果是通过 canvas
来实现的,所以我们首先需要学习一下如何通过 canvas
相关的 api 画出一个烟花来。在最初展示的例子中,我们的烟花其实就是一个圆,然后有一个拖尾的效果,并且会自动上升到天空中,最后会有一个爆炸效果。
那么我们先来看一下如何实现一个圆?
首先需要在页面中添加一个 canvas
标签,然后通过 js
获取到这个 canvas
标签,并且获取到它的 2d 属性,我们这里只涉及到 2d 的内容,因此可以通过 getContext('2d')
获取到相关的对象,当然 getContext()
中的参数也支持其它的内容,但是涉及的内容太复杂,这里就不做过多介绍了。
获取到 canvas
画布后,我们就需要借助 canvas
上 2d 属性相关的方法来绘制圆了,下面一起来看一下相关的代码。
我们先在页面中添加一个 canvas
标签,html
如下:
<canvas id="canvas"></canvas>
添加完 canvas
标签后,我们就需要来写 js
代码了,如下:
// 获取到 canvas 元素
const canvas = document.getElementById('canvas');
// 获取 2d 属性
const ctx = canvas.getContext('2d');
// 设置 canvas 的宽高,如果不设置,默认是的宽是300,高是100
canvas.width = innerWidth;
canvas.height = innerHeight;
// 开始绘制
ctx.beginPath();
ctx.fillStyle = 'red';
ctx.arc(100, 100, 30, 0, Math.PI * 2);
ctx.fill();
ctx.closePath();
上述代码中都添加了相关的注释,理解起来比较简单。需要注意的是,开始绘制时要使用 ctx.beginPath()
,这主要是为了让前一次绘制和后一次绘制的图形分开,大家可以试试不使用这个方法会产生什么样的效果。
然后我们还使用到了 ctx.arc()
方法,它主要就是用来绘制一个圆的,其中的参数有共有6个,前5个是必填项,最后一个是选填项,使用方法是这样的:
ctx.arc(x, y, radius, startAngle, endAngle, anticlockwise);
x 表示:圆弧中心(圆心)的 x 轴坐标。
y 表示:圆弧中心(圆心)的 y 轴坐标。
radius 表示:圆弧的半径。
startAngle 表示:圆弧的起始点,x 轴方向开始计算,单位以弧度表示。
endAngle 表示:圆弧的终点, 单位以弧度表示。
anticlockwise 表示(可选):可选的
Boolean
值 ,如果为true
,逆时针绘制圆弧,反之,顺时针绘制。
知道了 ctx.arc()
的使用方法后,我们最终通过上述代码实现的效果如下图所示:
前面说了我们绘制烟花是用圆来绘制,这里是为了讲解 ctx.arc()
的使用,因此设置的圆弧半径为30,实际开发中我们只需要将半径设置为3即可,因为烟花本身弹射出来时并不大。
接下来我们就需要让这个烟花动起来了,那么该怎么实现呢?其实也很好解决,之前我们做动画效果,一般都是使用 setInterval
或者 setTimeout
这两个方法,但是现在浏览器还有一个 requestAnimationFrame
方法可以让我们使用,我们可以看一下相关的介绍:
window.requestAnimationFrame()
告诉浏览器——你希望执行一个动画,并且要求浏览器在下次重绘之前调用指定的回调函数更新动画。该方法需要传入一个回调函数作为参数,该回调函数会在浏览器下一次重绘之前执行。
简单来说,我们使用 requestAnimationFrame
方法时不需要自己去设定一个执行的时间,浏览器会自动去执行相关的动画,并且这个方法的使用也很简单,下面我们一起来看一下如何让这个烟花向天空弹射出来。
烟花发射
简单来说,如果一个元素想要移动,那么就需要改变它在 x轴 或者 y轴 上面的值,通过不断的改变这两个值,对应的元素就会动起来了,下面我们先来看一下相关的代码,如下:
const canvas = document.getElementById('canvas');
// 获取 2d 属性
const ctx = canvas.getContext('2d');
// 设置 canvas 的宽高,如果不设置,默认是的宽是300,高是100
canvas.width = innerWidth;
canvas.height = innerHeight;
// 随机生成烟花出现的 x轴 位置
let x = Math.random() * canvas.width | 0;
let y = canvas.height;
// 设置一个重力参数,用于确保烟花能够到达页面的最高位置
let vel = -(Math.random() * Math.sqrt(canvas.height) / 3 + Math.sqrt(4 * canvas.height) / 2) / 5;
// 随机生成一个颜色
let color = `hsl(${Math.random() * 360 | 0}, 100%, 60%)`;
上面这些都是一些基础的准备数据,其中的 Math.random() * canvas.width | 0
意思是生成一个随机值为 0 到 canvas
宽度的值,后面的竖线是 js
中的二进制运算符,之所以用竖线运算符,是为了帮助我们获取到整数,因为 Math.random()
随机生成的是从0到1直接的小数,我们不需要小数位,因此这里直接使用竖线运算符是最为方便的,当然也可以使用 parseInt()
或者其它的方法也都是可以的。
除了 Math.random()
,还使用到了 Math.sqrt()
,那 Math.sqrt() 又是个什么东西呢?这其实是三角函数中用来开平方的,后面我们一起来学习一下三角函数相关的知识,这里知道怎么用就可以了。
有了上述的基本内容后,我们就可以开始来添加动画相关的内容了,我们先修改一下之前的代码,如下:
function draw() {
ctx.globalAlpha = 1;
// 开始绘制
ctx.beginPath();
ctx.fillStyle = color;
ctx.arc(x, y, 3, 0, Math.PI * 2);
ctx.fill();
ctx.closePath();
}
在前面的代码中,我们是直接绘制一个圆,这里我们将这个绘制的方法封装成一个 draw
方法,方便我们后续的使用,接下来我们就需要有一个更新的方法了,主要用于不断的更新绘制圆中的 y轴 信息,这样圆球就能动起来了,代码如下:
function update() {
y += vel;
// 摩擦系数
vel += 0.04;
// 当达到最高点时,需要删除当前烟花,重新再绘制一个新的
if (vel >= 0) {
x = Math.random() * canvas.width | 0;
y = canvas.height;
vel = -(Math.random() * Math.sqrt(canvas.height) / 3 + Math.sqrt(4 * canvas.height) / 2) / 5;
color = `hsl(${Math.random() * 360 | 0}, 100%, 60%)`;
}
}
我们定义了一个 update
方法,在这个方法中,通过改变 y轴 的坐标,并且不断改变最开始定义的圆球重力参数,从而使圆球也就是烟花能够到达屏幕最高点,并且在最高点时需要重新绘制一个新的烟花。我们一起来看一下最后关于动画相关的代码,如下:
function animate() {
requestAnimationFrame(animate);
ctx.globalAlpha = 0.1;
ctx.fillStyle = 'black';
ctx.fillRect(0, 0, canvas.width, canvas.height);
update();
draw();
}
animate();
在这个 animate
中,我们通过 requestAnimationFrame
方法调用 animate
本身,并且不断的更新和重绘这个烟花,在 animate
中,我们没有使用 clearRect()
而是 fillRect()
,这样做的好处就是可以让烟花在页面中有一个拖尾效果,上述代码最终实现的效果如下图所示:
到这里,我们的烟花也完成,还剩下什么呢?对了,就是烟花爆炸后的效果,我们需要创建一个爆炸的粒子效果来代替烟花的爆炸效果,那么该怎么实现呢?
别急,还记得我们在前面讲的关于三角函数
的内容吗?要制作粒子爆炸效果,就需要用到大量的三角函数
的知识,因此接下来我们就先来学习一下三角函数
相关的知识吧!
三角函数
三角函数
是数学中常见的一类关于角度的函数,一般我们在初中都学过相关的知识点,而在 js
中,三角函数
的运用是怎样的呢?
首先来看一下勾股定理:
根据勾股定理可以得出:
角度α | 0° | 30° | 45° | 60° | 90° | 120° | 135° | 150° | 180° | 270° | 360° |
---|---|---|---|---|---|---|---|---|---|---|---|
弧度 | o | π/6 | π/4 | π/3 | π/2 | 2π/3 | 3π/4 | 5π/6 | π | 3π/2 | 2π |
sinα | o | 1/2 | √2/2 | √3/2 | 1 | √3/2 | √2/2 | 1/2 | 0 | -1 | 0 |
cosα | 1 | √3/2 | √2/2 | 1/2 | 0 | -1/2 | -√2/2 | -√3/2 | -1 | 0 | 1 |
tanα | o | √3/3 | 1 | √3 | - | -√3 | -1 | -√3/3 | 0 | - | 0 |
通过上面表格中的换算,我们可以得知每个角度对应的弧度,因为在 js
中是没有角度的,所以只能用弧度来计算,因此我们就需要用到对应的换算公式。
我们再来看两个其它的 API
:
Math.pow() 求平方/立方
Math.sqrt() 开平方
在上图中,如果我们要求 c边 的长度,按照勾股定理,就是前面列出的计算方式,在 js
中则需要使用前面这两个 API
来实现,代码如下:
c = Math.sqrt(Math.pow(a, 2) + Math.pow(b, 2))
a的平方加b的平方,然后开平方就能得到c的值。
更多关于三角函数的更多内容,可以点击这里进行查看,以上内容也是来自这篇文章。这里就不做过多的介绍了,毕竟我自己也不是很熟悉<( ̄︶ ̄)>
有了三角函数
相关的知识点,接下来就需要制作粒子爆炸效果了。
粒子爆炸
要实现粒子爆炸的效果,其实也很简单。跟前面的烟花效果一样,要实现爆炸效果也是改变某个方法的值就可以了,当烟花弹射到最高点时,就需要动态的产生一个粒子了。
我们先来实现一个粒子的绘制,代码如下:
class Particle {
constructor(x, y, color, canvas, ctx) {
this.x = x;
this.y = y;
this.color = color;
// 随机生成粒子炸开的方向
this.vx = (0.5 - Math.random()) * 100;
this.vy = (0.5 - Math.random()) * 100;
// 随机生成粒子有消息,来判断每个粒子何时熄灭
this.age = Math.random() * 100 | 0;
}
draw() {
ctx.globalAlpha = 1;
ctx.beginPath();
ctx.fillStyle = this.color;
ctx.arc(this.x, this.y, 1, 0, Math.PI * 2);
ctx.fill();
}
update() {
// 同时改变粒子的垂直方向的值,确保粒子会向下运动
x += this.vx / 20;
y += this.vy / 20;
this.vy++;
this.age--;
}
}
在 Particle
中, 除了通过前面的烟花传过来的烟花的 x轴 和 y轴 以及烟花的颜色外,我们还单独定义了两个变量,这两个变量主要用于随机生成粒子炸开的方向。通过不断的修改 x轴 和 y轴 的值,从而实现粒子在空中炸开的效果。
最终我们通过面向对象的方法将代码组织起来,从而实现最初展示给大家看到的烟花效果。
总结
通过学习 canvas
相关的 API
实现了基本的圆,以及烟花的生成;通过 requestAnimationFrame
方法实现动画效果,从而完成了烟花的发射;通过学习三角函数相关的知识,实现了粒子的爆炸效果,并且还学习了 js
中的二进制运算,从而简化了我们获取随机整数的方法。
这一节是为我们后续学习更多关于 canvas
绚丽效果打下基础,在后续更多的 canvas
案例中,我们还会反复用到三角函数相关的知识点。
最后,如果这篇文章有帮助到你,❤️关注+点赞❤️鼓励一下作者,谢谢大家