js透明背景烟花效果并适配移动端,附带详细注释。
效果:

Js及HTML:
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>透明背景烟花效果适配移动端</title>
<style>
body, html{margin:0;padding:0;overflow:hidden;}
.topic {position:relative;width:100%;height:50vh;background:url(https://blog.csdn.net/xxx.jpg) no-repeat top left;background-size:100% 100%;}
#fireworks{position:absolute;top:0;left:0;width:100%;height:100%;}
</style>
</head>
<body>
<div class="topic">
<canvas id="fireworks"></canvas>
</div>
<script>
// 烟花效果
const canvas = document.getElementById('fireworks');
const ctx = canvas.getContext('2d');
// 响应式参数
let isSmallScreen = window.innerWidth < 860;
// 设置画布尺寸,使其与父容器.topic的尺寸保持一致 并在窗口大小变化时调整已有粒子的位置
function resizeCanvas() {
// 获取指定父级 topic 元素
const topicElement = document.querySelector('.topic')
// 设置 canvas 尺寸
canvas.width = topicElement.clientWidth
canvas.height = topicElement.clientHeight
// 检测屏幕尺寸变化
const newIsSmallScreen = window.innerWidth < 860;
if (newIsSmallScreen !== isSmallScreen) {
isSmallScreen = newIsSmallScreen;
updateFireworksDensity();
}
}
// 根据屏幕尺寸更新烟花发射密度和粒子数量 小屏幕设备减少烟花密度和粒子数量以优化性能
function updateFireworksDensity() {
if (!fireworksManager) return;
// 清除现有定时器
clearInterval(fireworksManager.autoLaunchInterval);
// 根据屏幕尺寸设置不同的发射间隔和粒子数量
if (isSmallScreen) {
// 小屏幕:减少发射频率和粒子数量
fireworksManager.autoLaunchInterval = setInterval(() => {
fireworksManager.launchFirework();
}, 2500 + Math.random() * 5000); // 每2.5-5秒发射一个烟花
// 修改爆炸函数,减少粒子数量
fireworksManager.explode = (x, y, color) => {
for (let i = 0; i < 60 + Math.random() * 60; i++) {
fireworksManager.particles.push(new Particle(x, y, color));
}
}
} else {
// 大屏幕:恢复原始设置
fireworksManager.autoLaunchInterval = setInterval(() => {
fireworksManager.launchFirework();
}, 100 + Math.random() * 1500); // 每0.1-2.3秒发射一个烟花
// 恢复原始爆炸函数
fireworksManager.explode = (x, y, color) => {
for (let i = 0; i < 120 + Math.random() * 120; i++) {
fireworksManager.particles.push(new Particle(x, y, color));
}
}
}
}
// 烟花粒子类表示单个粒子(火箭或爆炸碎片)
class Particle {
constructor(x, y, color, isRocket = false) {
this.x = x; // 粒子的x坐标(初始位置)
this.y = y; // 粒子的y坐标(初始位置)
this.color = color; // 粒子颜色(HSL格式,如'hsl(120, 100%, 70%)')
this.isRocket = isRocket; // 是否为火箭(true: 火箭,false: 爆炸碎片)
this.opacity = 1; // 粒子透明度(初始为完全不透明)
this.age = 0; // 粒子生命周期计时器(帧计数)
this.lifeSpan = 80 + Math.random() * 40; // 粒子总寿命(80~120帧)
if (this.isRocket) {
// 火箭参数
this.velocity = {
x: (Math.random() - 0.5) * 2, // 水平速度(-1~1之间随机值,控制横向偏移)
y: Math.random() * -6 - 10 // 垂直速度(-16~-10之间随机值,负值表示向上发射)
};
this.gravity = 0.07; // 重力加速度(控制火箭上升高度和下落速度)
this.friction = 0.99; // 摩擦系数(速度衰减因子,值越小减速越快)
this.minExplosionHeight = 0.1; // 最小爆炸高度(相对于画布高度的比例)
this.maxExplosionHeight = 0.9; // 最大爆炸高度(相对于画布高度的比例)
} else {
// 爆炸粒子参数 - 线条效果
const angle = Math.random() * Math.PI * 2; // 随机角度(0~360度)
const speed = Math.random() * 8 + 5; // 随机速度(5~13之间,控制爆炸扩散范围)
this.velocity = {
x: Math.cos(angle) * speed, // 水平速度分量
y: Math.sin(angle) * speed // 垂直速度分量
};
this.gravity = 0.03; // 减小重力,使粒子飞更远
this.friction = 0.96; // 增加摩擦,使速度衰减更慢
this.size = Math.random() * 2 + 1; // 线条粗细(1~3之间随机值)
this.length = Math.random() * 15 + 10; // 线条长度(10~25之间随机值,决定爆炸视觉效果)
this.lastX = x; // 记录上一帧位置(用于绘制线条)
this.lastY = y; // 记录上一帧位置(用于绘制线条)
}
}
//更新粒子状态(位置、速度、生命周期等)
update() {
this.age++;
if (this.isRocket) {
// 火箭更新
this.velocity.x *= this.friction; // 水平速度衰减
this.velocity.y *= this.friction; // 垂直速度衰减
this.velocity.y += this.gravity; // 应用重力
this.x += this.velocity.x; // 更新x位置
this.y += this.velocity.y; // 更新y位置
} else {
// 爆炸粒子更新 - 线条效果
this.lastX = this.x; // 保存上一帧位置
this.lastY = this.y; // 保存上一帧位置
this.velocity.x *= this.friction; // 水平速度衰减
this.velocity.y *= this.friction; // 垂直速度衰减
this.velocity.y += this.gravity; // 应用重力
this.x += this.velocity.x; // 更新x位置
this.y += this.velocity.y; // 更新y位置
// 随时间减少不透明度,实现淡出效果
this.opacity = 1 - (this.age / this.lifeSpan);
if (this.opacity < 0) this.opacity = 0;
}
}
// 绘制粒子(火箭或爆炸碎片)
draw() {
ctx.save();
ctx.globalAlpha = this.opacity; // 设置透明度
if (this.isRocket) {
// 绘制火箭(三角形形状)
ctx.beginPath();
ctx.moveTo(this.x, this.y + 5);
ctx.lineTo(this.x + 2, this.y - 5);
ctx.lineTo(this.x - 2, this.y - 5);
ctx.closePath();
ctx.fillStyle = this.color;
ctx.fill();
// 绘制火箭尾焰
ctx.beginPath();
ctx.moveTo(this.x, this.y + 5);
ctx.lineTo(this.x + 1, this.y + 10);
ctx.lineTo(this.x - 1, this.y + 10);
ctx.closePath();
ctx.fillStyle = 'rgba(255, 200, 0, 0.8)';
ctx.fill();
} else {
// 绘制线条形状的爆炸粒子
ctx.beginPath();
ctx.moveTo(this.lastX, this.lastY); // 线条起点(上一帧位置)
ctx.lineTo(this.x, this.y); // 线条终点(当前位置)
ctx.lineWidth = this.size; // 设置线条粗细
ctx.strokeStyle = this.color; // 设置线条颜色
ctx.stroke(); // 绘制线条
// 线条末端添加一个小点,增强视觉效果
ctx.beginPath();
ctx.arc(this.x, this.y, this.size / 20, 0, Math.PI * 2);
ctx.fillStyle = this.color;
ctx.fill();
}
ctx.restore();
}
}
// 烟花管理器 统筹管理所有粒子的生命周期和动画循环
class FireworksManager {
constructor() {
this.particles = []; // 存储所有粒子的数组
this.autoLaunchInterval = null; // 自动发射定时器
}
// 生成随机颜色
getRandomColor() {
return `hsl(${Math.floor(Math.random() * 360)}, 100%, 70%)`;
}
// 发射烟花火箭
launchFirework(x = Math.random() * canvas.width, y = canvas.height) {
this.particles.push(new Particle(x, y, this.getRandomColor(), true));
}
// 触发爆炸效果
explode(x, y, color) {
const count = isSmallScreen ? 60 + Math.random() * 60 : 120 + Math.random() * 120;
for (let i = 0; i < count; i++) {
this.particles.push(new Particle(x, y, color));
}
}
//启动自动发射烟花
startAutoLaunch() {
updateFireworksDensity()
}
//更新所有粒子状态,处理边界检测和爆炸条件
update() {
for (let i = this.particles.length - 1; i >= 0; i--) {
const particle = this.particles[i];
particle.update(); // 更新粒子状态
// 确保粒子不会超出画布范围
if (particle.y > canvas.height) {
particle.y = canvas.height;
// 如果是火箭,到达底部后爆炸
if (particle.isRocket) {
this.explode(particle.x, particle.y, particle.color);
this.particles.splice(i, 1); // 移除火箭粒子
}
}
// 修改火箭爆炸条件
if (particle.isRocket) {
// 检查火箭是否在Canvas高度范围内且开始下落
const inRange = particle.y < canvas.height * particle.maxExplosionHeight && particle.y > canvas.height * particle.minExplosionHeight;
// 如果火箭超出Canvas顶部,强制在顶部爆炸
if ((inRange && particle.velocity.y >= 0) || particle.y < 0) {
// 火箭达到最高点并开始下落时爆炸
this.explode(particle.x, particle.y, particle.color);
this.particles.splice(i, 1); // 移除火箭粒子
}
}
// 移除生命周期结束的粒子(非火箭且透明度为0)
if (!particle.isRocket && particle.opacity <= 0) {
this.particles.splice(i, 1);
}
}
}
// 绘制所有粒子
draw() {
ctx.clearRect(0, 0, canvas.width, canvas.height); // 清空画布
for (const particle of this.particles) {
particle.draw(); // 绘制每个粒子
}
}
// 循环
animate() {
this.update();
this.draw();
requestAnimationFrame(this.animate.bind(this));
}
// 启动烟花效果
start() {
this.animate(); // 启动动画循环
this.startAutoLaunch(); // 启动自动发射
}
}
// 初始化烟花管理器
const fireworksManager = new FireworksManager();
// 设置画布尺寸并监听窗口大小变化
resizeCanvas();
window.addEventListener('resize', resizeCanvas);
// 播放烟花
fireworksManager.start();
// 点击事件 - 点击发射烟花
canvas.addEventListener('click', (e) => {
const rect = canvas.getBoundingClientRect();
fireworksManager.launchFirework(
e.clientX - rect.left,
e.clientY - rect.top
)
})
</script>
</body>
</html>
7860

被折叠的 条评论
为什么被折叠?



