前言
年关将至,***市区又禁放烟花,但是又非常想看烟花肿么办呢,我们可以用 js 在网页上实现烟花的效果,下面我们将功能进行拆解。
步骤分解
将烟花效果拆分 2 个阶段:
- 烟花上升过程
- 上升到某个点后,爆炸生成碎片逐渐消失效果
实现思路确定好开始用代码去实现效果
烟花上升效果
粒子上升
通过定时器不断的去更新粒子y
轴值,同時增加vy
达到一个加速的感觉,age是当前粒子存活时间。
ini
// 烟花粒子
class Firework {
constructor({ x, y, vy, age }) {
this.x = x;
this.y = y;
this.vy = vy || random(0, 1);
this.age = age || random(200, 400);
}
update() {
this.y -= this.vy;
this.vy += 0.01;
this.age--;
this.draw();
}
draw() {
ctx.fillStyle = "#fff";
ctx.beginPath();
ctx.arc(this.x, this.y, 2, 0, Math.PI * 2, 0);
ctx.closePath();
ctx.fill();
}
}
let firework = new Firework({
x: canvas.width * Math.random(),
y: canvas.height,
});
setInterval(() => {
ctx.clearRect(0, 0, canvas.width, canvas.height); // 清除矩形
if (firework) {
firework.update();
firework.draw();
if (firework.age < 0) {
firework = null;
}
}
}, 60 / 1000);
从上面效果图可以看到粒子上升的过程非常单调,如果增加流光尾迹效果更加真实、炫酷。
两种方式实现粒子拖尾效果
- 幕布覆盖
- 维护运动轨迹记录帧+渐变实现
幕布覆盖
在定时器里面把清除矩形方法(clearRect)去掉,通过带有透明度的幕布进行覆盖即可实现拖尾效果。加上这两行代码。
ctx.fillStyle = "rgba(0,0,0,0.1)"; // 填充颜色
ctx.fillRect(0, 0, canvas.width, canvas.height); // 填充矩形
ini
setInterval(() => {
// ctx.clearRect(0, 0, canvas.width, canvas.height); // 清除矩形
ctx.fillStyle = "rgba(0,0,0,0.1)"; // 填充颜色,透明度越小尾迹越长
ctx.fillRect(0, 0, canvas.width, canvas.height); // 填充矩形
if (firework) {
firework.update();
firework.draw();
if (firework.age < 0) {
firework = null;
}
}
}, 60 / 1000);
维护运动轨迹记录帧+渐变实现
定时器里面添加清除矩形方法,清除上一帧,在Firework
内部维护trailingArray
数组用来记录之前每一帧,超过三十个时剔除掉之前帧。在每次绘制时将trailingArray
首尾帧连接,并设置渐变即可实现拖尾效果。
kotlin
class Firework {
constructor({ x, y, vy, age, ctx, isTailingEnabled }) {
// ... 之前代码
this.trailingArray = [];
}
update() {
// ... 之前代
this.trailingArray.unshift({ x: this.x, y: this.y }); // 在拖尾的开始添加新点
if (this.trailingArray.length > 30) {
this.trailingArray.pop();
}
this.draw();
}
draw() {
// ... 之前代
// 绘制拖尾
this.updateTrailing();
}
updateTrailing() {
ctx.beginPath();
var lastItem = this.trailingArray[this.trailingArray.length - 1];
var gradient = ctx.createLinearGradient(
this.x,
this.y,
lastItem.x,
lastItem.y
); // 创建渐变对象
gradient.addColorStop(0, "#fff"); // 在起点处设置颜色为同色
gradient.addColorStop(1, "transparent"); // 在终点处设置颜色为透明
ctx.lineWidth = 2;
ctx.moveTo(this.x, this.y); // 从当前粒子位置开始绘制
ctx.lineTo(lastItem.x, lastItem.y); // 连接每个拖尾点
ctx.strokeStyle = gradient; // 设置线条颜色为粒子的颜色
ctx.stroke(); // 绘制线条到最后一个拖尾点
}
}
// firework粒子实例
setInterval(() => {
ctx.clearRect(0, 0, canvas.width, canvas.height); // 清除矩形
if (firework) {
firework.update();
firework.draw();
if (firework.age < 0) {
firework = null;
}
}
}, 60 / 1000);
以上就是用不同的方式去实现拖尾效果,接下来我们要实现的就是达到某个高度时爆炸的效果。
烟花爆炸
以圆为切入点,循环把每个点绘制成圆,然后不断增加半径长度即达到烟花散开的效果。
ini
let count = 100,rx = 600, ry = 300,r = 0;
function draw() {
for (let i = 0; i < count; i++) {
let angle = (360 / count) * i; // 求出各个点的角度
let radians = (angle * Math.PI) / 180; // 转换为弧度制
let moveX = rx + Math.cos(radians) * r;
let moveY = ry + Math.sin(radians) * r;
ctx.fillStyle = "#fff";
ctx.beginPath();
ctx.arc(moveX, moveY, 2, Math.PI * 2, false);
ctx.closePath();
ctx.fill();
}
}
let timer = setInterval(() => {
ctx.clearRect(0, 0, canvas.width, canvas.height);
r += 0.4;
draw();
}, 60 / 1000);
上图已经实现烟花碎片运动的效果,但是效果看起来很不真实,碎片移动太过整齐、无重力效果。我们可将烟花碎片粒子封装成类,在初始化的时候用不同的增长速度让烟花碎片进行无规则的运动,并在y轴稍微增加一些值让碎片看起来有下坠感,更接近真实效果。
kotlin
class Ball {
constructor({ fillStyle, rx, ry, ctx, v, r, radians }) {
this.ctx = ctx;
this.fillStyle = fillStyle || getRandomColor();
this.rx = rx; // 圆心x位置
this.ry = ry; // 圆心y位置
this.r = r || 0; // 初始半径
this.radians = radians || 0; // 弧度制
this.v = v || 0.2; // 速率
this.yh = 0; // 增长值
this.vy = 0.04; // 增长值速度
}
update() {
// 1. 每个碎片实例 new 的时候传入不同的 this.v 增长速度;
this.r += this.v;
// 2. 需要碎片有下坠感,需要增加yh单独y的增长高度,加在原来的moveY上就达到下坠的感觉
// 2. 需要加速的感觉需要单独维护 vy 增长速度
this.yh += this.vy;
this.vy += 0.01;
let moveX = this.rx + Math.cos(this.radians) * this.r;
let moveY = this.ry + this.yh + Math.sin(this.radians) * this.r;
this.draw(moveX, moveY);
}
draw(moveX, moveY) {
this.ctx.fillStyle = this.fillStyle;
this.ctx.beginPath();
this.ctx.arc(moveX, moveY, 2, Math.PI * 2, false);
this.ctx.closePath();
this.ctx.fill();
}
}
以上就是两大核心的实现思路,只要我们将这些功能组合起来,就形成一个完整的烟花啦。
结尾
到这里,本文的所有内容就结束了。主要分析两个难点实现思路,感兴趣的同学可以领取源码在原来基础上拓展。
- 粒子上升拖尾效果
- 烟花碎片爆炸效果
最后希望看到这里的同学都有所收获,也希望同学能给一个小小的赞🌹🌹🌹,需要源码,关注公众号三哈同学
,回复「烟花效果」领取。