Canvas之粒子烟花

Canvas之粒子烟花

js 复制代码
ctx.translate(0, canvas.height)
ctx.scale(1, -1);
class Fireworks{
    constructor(x, y) {
            this.x = x;
            this.y = y;
            this.r = 6;
            this.opacity = 1;
        }
        draw() {
            this.opacity = this.opacity < 0.2 ? 0.2 : this.opacity;
            for (let i = 0; i < 100; i++) {
                const ball = new Ball(this.x, this.y - i, this.r - i / 20, `rgba(200,200,50,${this.opacity - i / 100})`);
                ball.draw();
            }
        }
}
//小球
class Ball {
    constructor(x, y, r, color) {
        this.x = x;
        this.y = y;
        this.r = r;
        this.color = color;
    }
    draw() {
        ctx.save();
        ctx.beginPath();
        ctx.fillStyle = this.color;
        ctx.arc(this.x, this.y, this.r, 0, Math.PI * 2);
        ctx.fill();
        ctx.restore();
    }

}
//烟花移动
function move() {
    ctx.clearRect(0, 0, canvas.width, canvas.height);
    requestAnimationFrame(move);
}

move()

需要有烟花主体,上升的小球,烟花绽放的粒子。这些都是一个个对象,所以使用面向对象的结构。

烟花是由一排小球组成。先调整坐标系,变成向上的坐标系。这些小球的y坐标不断减小,半径也在不断减小,达成了尖角的形状。然后随着时间不断上升。

小球需要与圆相关的参数,当然不一定必须是圆形。绘制函数里同样需要保存和重置。

js 复制代码
const fireworksArray = [];//存放烟花的数组
let sum = 0;
function move(){
    if (sum % 60 == 0) {
        const x = Math.random() * canvas.width / 2 + canvas.width / 4;
        const y = Math.random() * 50 + 10;
        const fire = new Fireworks(x, y);
        fireworksArray.push(fire);
    }
    if (fireworksArray.length % 3 == 0) {
        const fireworks = fireworksArray.shift();
        bombArray.push(fireworks);
    }
}

在烟花上升的过程中用一个数组进行控制,每过一秒都会进行位置的变化,这个位置可以控制在一定范围内。每次都加入数组中,当数组中元素超过三个时,取出最前面的一个。

js 复制代码
function randomNumber(min, max) {
    return Math.random() * (max - min + 1) + min;
}
class Fireworks{
    //爆炸
    bomb() {
        if (this.particles.length === 0) {
            const hd = Math.PI * 2 / this.count;
            const color = `rgba(${randomNumber(0, 255)},${randomNumber(0, 255)},${randomNumber(0, 255)},0.8)`;
            for (let i = 0; i < this.count; i++) {
                //角度,亮度,饱和度
                // const color = `hsl(${ Math.random() * 360 }, 50 %, 50 %)`;
                const dirx = Math.cos(hd * i) * randomNumber(0, 3);
                const diry = Math.sin(hd * i) * randomNumber(0, 3);
                const particle = new Particle(this.x, this.y, dirx, diry, color, i % 2 === 0);
                this.particles.push(particle);
                particle.draw();
            }
        } else {
            this.particles.forEach(particle => {
                particle.update();
            });
        }
    }
}
//爆炸粒子
class Particle {
    constructor(x, y, dirx, diry, color, type) {
        this.x = x;
        this.y = y;
        this.r = 2;
        this.dirx = dirx;
        this.diry = diry;
        this.color = color;
        this.type = type;
    }
    draw() {
        if (this.type) {
            ctx.save();
            ctx.beginPath();
            ctx.fillStyle = this.color;
            ctx.arc(this.x, this.y, this.r, 0, Math.PI * 2);
            ctx.fill();
            ctx.restore();
        } else {
            this.type = !this.type;
        }
    }
    //更新粒子的位置
    update() {
        this.x += this.dirx;
        this.y += this.diry;
        this.dirx *= 0.99;
        this.diry *= 0.99;
        this.draw();
    }
}
//烟花移动
const fireworksArray = [];//存放烟花的数组
const bombArray = [];//存放爆炸的数组
let sum = 0;
function move() {
    ctx.clearRect(0, 0, canvas.width, canvas.height);

    if (sum % 60 == 0) {
        const x = Math.random() * canvas.width / 2 + canvas.width / 4;
        const y = Math.random() * 50 + 10;
        const fire = new Fireworks(x, y);
        fireworksArray.push(fire);
    }
    if (fireworksArray.length % 3 == 0) {
        const fireworks = fireworksArray.shift();
        bombArray.push(fireworks);
    }
    if (bombArray.length % 3 == 0) {
        bombArray.shift();
    }
    fireworksArray.forEach((fire, i) => {
        fire.draw()
        fire.y += 5;//控制速度
        fire.opacity -= 0.01;
    });
    bombArray.forEach((particle, i) => {
        particle.bomb();
    })
    sum++;
    requestAnimationFrame(move);
}

move()

爆炸的粒子在烟花的小球数组移出时进行爆炸。爆炸的粒子包括粒子的绘制和更新位置。

定义好粒子的个数,然后根据圆弧的形式第一次进行绘制,之后进行更新。粒子的位置,方向,颜色都是随机生成的。可以定义一个生成随机最小最大值的函数。

同样,当爆炸个数超过三个时进行移除,不超过三个时进行爆炸函数的调用。

相关推荐
Hilaku31 分钟前
我用 Gemini 3 Pro 手搓了一个并发邮件群发神器(附源码)
前端·javascript·github
IT_陈寒32 分钟前
Java性能调优实战:5个被低估却提升30%效率的JVM参数
前端·人工智能·后端
快手技术33 分钟前
AAAI 2026|全面发力!快手斩获 3 篇 Oral,12 篇论文入选!
前端·后端·算法
颜酱34 分钟前
前端算法必备:滑动窗口从入门到很熟练(最长/最短/计数三大类型)
前端·后端·算法
全栈前端老曹43 分钟前
【包管理】npm init 项目名后底层发生了什么的完整逻辑
前端·javascript·npm·node.js·json·包管理·底层原理
HHHHHY1 小时前
mathjs简单实现一个数学计算公式及校验组件
前端·javascript·vue.js
boooooooom1 小时前
Vue3 provide/inject 跨层级通信:最佳实践与避坑指南
前端·vue.js
一颗烂土豆1 小时前
Vue 3 + Three.js 打造轻量级 3D 图表库 —— chart3
前端·vue.js·数据可视化
青莲8431 小时前
Android 动画机制完整详解
android·前端·面试
iReachers1 小时前
HTML打包APK(安卓APP)中下载功能常见问题和详细介绍
前端·javascript·html·html打包apk·网页打包app·下载功能