Files
chatroom/public/js/effects/fireworks.js
T

141 lines
4.2 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* 文件功能:聊天室烟花特效
*
* 使用 Canvas 粒子系统在全屏播放多发烟花爆炸动画。
* 特效总时长约 4 秒,结束后自动清理并回调。
*/
const FireworksEffect = (() => {
// 粒子类:模拟一个爆炸后的发光粒子
class Particle {
constructor(x, y, color) {
this.x = x;
this.y = y;
this.color = color;
// 随机方向和速度
const angle = Math.random() * Math.PI * 2;
const speed = Math.random() * 6 + 2;
this.vx = Math.cos(angle) * speed;
this.vy = Math.sin(angle) * speed;
this.alpha = 1;
this.gravity = 0.12;
this.decay = Math.random() * 0.012 + 0.012; // 透明度每帧衰减量
this.radius = Math.random() * 3 + 1;
}
/** 每帧更新粒子位置和状态 */
update() {
this.vy += this.gravity;
this.x += this.vx;
this.y += this.vy;
this.vx *= 0.98; // 空气阻力
this.vy *= 0.98;
this.alpha -= this.decay;
}
/** 绘制粒子 */
draw(ctx) {
ctx.save();
ctx.globalAlpha = Math.max(0, this.alpha);
ctx.fillStyle = this.color;
ctx.beginPath();
ctx.arc(this.x, this.y, this.radius, 0, Math.PI * 2);
ctx.fill();
ctx.restore();
}
}
// 预定义烟花颜色组
const COLORS = [
"#ff4444",
"#ff8800",
"#ffdd00",
"#44ff44",
"#44ddff",
"#8844ff",
"#ff44cc",
"#ffffff",
"#ffaaaa",
"#aaffaa",
"#aaaaff",
"#ffffaa",
];
/**
* 发射一枚烟花,返回粒子数组
*
* @param {number} x 爆炸中心 x
* @param {number} y 爆炸中心 y
* @param {number} count 粒子数量
* @returns {Particle[]}
*/
function _burst(x, y, count) {
const color = COLORS[Math.floor(Math.random() * COLORS.length)];
const particles = [];
for (let i = 0; i < count; i++) {
particles.push(new Particle(x, y, color));
}
return particles;
}
/**
* 启动烟花特效
*
* @param {HTMLCanvasElement} canvas 全屏 Canvas
* @param {Function} onEnd 特效结束回调
*/
function start(canvas, onEnd) {
const ctx = canvas.getContext("2d");
const w = canvas.width;
const h = canvas.height;
const DURATION = 4500; // 总时长(ms
let particles = [];
let animId = null;
let launchCount = 0;
const MAX_LAUNCHES = 8; // 总共发射几枚烟花
// 定时发射烟花
const launchInterval = setInterval(() => {
if (launchCount >= MAX_LAUNCHES) {
clearInterval(launchInterval);
return;
}
const x = w * (0.15 + Math.random() * 0.7); // 避免贴近边缘
const y = h * (0.1 + Math.random() * 0.5); // 在屏幕上半区爆炸
const count = Math.floor(Math.random() * 40) + 60;
particles = particles.concat(_burst(x, y, count));
launchCount++;
}, 500);
const startTime = performance.now();
// 动画循环
function animate(now) {
// 清除画布(保持透明,不遮挡聊天背景)
ctx.clearRect(0, 0, w, h);
// 更新并绘制存活粒子(粒子自带 alpha 衰减,视觉上有淡出效果)
particles = particles.filter((p) => p.alpha > 0.02);
particles.forEach((p) => {
p.update();
p.draw(ctx);
});
if (now - startTime < DURATION) {
animId = requestAnimationFrame(animate);
} else {
// 特效结束:清空 canvas 后回调
clearInterval(launchInterval);
cancelAnimationFrame(animId);
ctx.clearRect(0, 0, w, h);
onEnd();
}
}
animId = requestAnimationFrame(animate);
}
return { start };
})();