用canvas整个烟花效果

闲来无事,想着随便捣鼓一点东西玩玩
说说思路:

一 需要一个粒子类

模拟每一个烟花粒子,粒子有横坐标,纵坐标,半径,速度,颜色等属性,以及绘制的方法。 颜色这里我加了个初始化的方法,加了透明度的处理。代码如下:


// 粒子类
import Explode from "./explode.js";

class Particle {
	constructor(ctx, x, y, radius = 5) {
		this.ctx = ctx;
		this.radius = radius;
		this.x = x;
		this.y = y;
		this.rgb = null;
		this.angle = 0;
	}
	// 初始化颜色
	initColor({ r, g, b }) {
		this.rgb = { r, g, b };

		this.color = "rgba(" + r + ", " + g + ", " + b + ", " + 1 + ")";
	}

	setColorAlpha(alpha) {
		this.color =
			"rgba(" +
			this.rgb.r +
			", " +
			this.rgb.g +
			", " +
			this.rgb.b +
			", " +
			1 +
			")";
	}

	paint() {
		this.ctx.beginPath();
		this.ctx.arc(this.x, this.y, this.radius, 0, 2 * Math.PI);
		this.ctx.fillStyle = this.color;
		this.ctx.fill();
		this.ctx.closePath();
		this.ctx.save();
	}
	del() {
		delete this;
	}
}

export default Particle;

2 需要一个发射的类

其实一次发射就是一个粒子从底部到天空的过程,这里我把发射类继承了粒子类。代码如下:


import Particle from "./particle.js";
import { getRandomColorRGB } from "./utils.js";
import Explode from "./explode.js";

// 随机生成一个发射角度 从-30度到30度
const getRandomAngle = () => {
	return Math.random() * (Math.PI / 3) - Math.PI / 6;
};

// 生成一个随机speed
const getRandomSpeed = () => {
	return Math.random() * 10 + 15;
};

export default class OneShot extends Particle {
	constructor(ctx, x, y) {
		super(ctx, x, y);
		this.speed = getRandomSpeed();
		this.initColor(getRandomColorRGB());
		this.paint();
		this.angle = getRandomAngle();
		this.explode = null;
		this.firstBoom = true;
	}

	launch() {
		if (this.speed <= 0) {
		// 如果速度小于等于0,就判断为到了爆炸阶段,进行爆炸的逻辑
		// 第一次进入爆炸逻辑,需生成一个爆炸实例
			if (this.explode) {
				this.explode.boom();
			} else {
				this.explode = new Explode(this.ctx, this.x, this.y, this.rgb);
			}
			// this.del();
			return;
		} else {
		// 发射阶段,发射角度是随机生成的(竖直方向往两边各偏移30度的范围内)
			this.x += this.speed * Math.sin(this.angle);
			this.y -= this.speed * Math.cos(this.angle);
			this.speed -= 0.4;
			this.paint();
		}
	}
}

3 需要一个爆炸类,模拟一次烟花发射到顶点时到爆炸效果。

每次到爆炸阶段,都可以往数组中添加num个粒子,起点一致往四周散开。还需要考虑到真实情况中的引力加速度。代码如下:


// 随机生成20-30个粒子,给不同的初始速度
import Particle from "./particle.js";

// 随机生成一个发射角度 从0度到360度
const getRandomAngle = () => {
	return Math.random() * (Math.PI * 2);
};

// 生成一个随机半径
const getRandomRadius = () => {
	return Math.random() * 3;
};

// 生成一个随机speed
const getRandomSpeed = () => {
	return Math.random() + 1;
};

export default class Explode {
	constructor(ctx, x, y, colorRGB, num = 30) {
		this.ctx = ctx;
		this.x = x;
		this.y = y;
		this.list = [];
		this.colorRGB = colorRGB;
		// 爆炸数量
		this.num = num;
		this.init();
	}
	init() {
		for (let i = 0; i < this.num; i++) {
			let p = new Particle(this.ctx, this.x, this.y, getRandomRadius());
			p.angle = getRandomAngle();
			p.initColor(this.colorRGB);
			p.speed = getRandomSpeed();
			this.list.push(p);
		}
	}
	
	boom() {
	// 爆炸阶段粒子移动逻辑,由爆炸中心往四周散开,角度随机从0-2*PI
		if (this.list.length === 0) return;

		for (let i = 0; i < this.list.length; i++) {
			const cur = this.list[i];
			cur.x += cur.speed * Math.sin(cur.angle);
			cur.y = cur.y + 0.2 - cur.speed * Math.cos(cur.angle);

			cur.paint();
			cur.speed -= 0.01;
			if (cur.speed < 0) {
				this.list.splice(i, 1);
			}
		}
	}
}

最后,再贴出index,html的代码:

<!--
 * @Author: error: error: git config user.name & please set dead value or install git && error: git config user.email & please set dead value or install git & please set dead value or install git
 * @Date: 2024-02-01 15:13:28
 * @LastEditors: error: error: git config user.name & please set dead value or install git && error: git config user.email & please set dead value or install git & please set dead value or install git
 * @LastEditTime: 2024-05-28 21:32:51
 * @FilePath: /practice/index.html
 * @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
-->
<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <style>
        *{
            padding: 0;
            margin: 0;
            box-sizing: border-box;
        }
        body {
            width: 100vw;
            height: 100vh;
            background-color: black;
        }

        .firework {
            width: 2px;
            height: 2px;
            background-color: red;
            position: absolute;
        }

        #myCanvas {
            width: 100%;
            height: 100%;
        }

        .bot {
            width: 100px;
            height: 40px;
            background: lightcoral;
            position: absolute;
            bottom: 0;
            left: 50%;
            margin-left: -50px;
        }
    </style>
</head>

<body>
    <canvas id="myCanvas" width="100px"></canvas>
    <div class="bot"></div>
</body>
<script src="./main.js" type="module"></script>

</html>

当然,主方法一定要贴出来:


import oneShot from "./oneShot.js";

// 获取 canvas 元素
const canvas = document.getElementById("myCanvas");
canvas.width = canvas.offsetWidth;
canvas.height = canvas.offsetHeight;
const ctx = canvas.getContext("2d");
window.W = canvas.width;
window.H = canvas.height;

const startX = canvas.width / 2;
const startY = canvas.height - 40 - 5;

const pList = [];
// 生成烟花粒子
for (let i = 0; i < 20; i++) {
	let p = new oneShot(ctx, startX, startY);
	pList.push(p);
}

// 主进程方法
let timer;

function main() {
	// ctx.clearRect(0, 0, W, H);
	pList.forEach((p) => p.launch());
	// 这里每次都绘制一个蒙层,来模拟拖尾的效果
	ctx.beginPath();
	ctx.fillStyle = "rgba(0,0,0,0.1)";
	ctx.fillRect(0, 0, W, H);
	ctx.closePath();
	ctx.save();
	timer = requestAnimationFrame(main);
}

requestAnimationFrame(main);

ok,废话不多说,看看效果:

烟花-canvas

老表,学废了没?

  • 3
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值