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

138 lines
4.1 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 粒子系统在全屏播放多发烟花爆炸动画。
* 粒子加大、加发光描边,在浅色背景上也清晰可见。
* 特效总时长约 5 秒,结束后自动清理并回调。
*/
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() * 7 + 3;
this.vx = Math.cos(angle) * speed;
this.vy = Math.sin(angle) * speed;
this.alpha = 1;
this.gravity = 0.12;
this.decay = Math.random() * 0.01 + 0.01; // 衰减略慢,显色更久
this.radius = Math.random() * 4 + 2; // 增大粒子半径
}
/** 每帧更新粒子位置和状态 */
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.shadowColor = this.color;
ctx.shadowBlur = 8;
ctx.beginPath();
ctx.arc(this.x, this.y, this.radius, 0, Math.PI * 2);
ctx.fill();
ctx.restore();
}
}
// 预定义烟花颜色组(饱和度高,避免和浅蓝背景撞色)
const COLORS = [
"#ff2200",
"#ff7700",
"#ffcc00",
"#00cc33",
"#cc00ff",
"#ff0088",
"#00aaff",
"#ff4488",
"#ff6600",
"#aaff00",
"#ff2255",
"#ffaa00",
];
/**
* 发射一枚烟花,返回粒子数组
*/
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 = 5000; // 总时长(ms
let particles = [];
let animId = null;
let launchCount = 0;
const MAX_LAUNCHES = 10; // 总发射枚数(增加)
// 定时发射烟花
const launchInterval = setInterval(() => {
if (launchCount >= MAX_LAUNCHES) {
clearInterval(launchInterval);
return;
}
const x = w * (0.1 + Math.random() * 0.8);
const y = h * (0.05 + Math.random() * 0.5);
const cnt = Math.floor(Math.random() * 50) + 80; // 每枚 80-130 粒子(增多)
particles = particles.concat(_burst(x, y, cnt));
launchCount++;
}, 450);
const startTime = performance.now();
// 动画循环
function animate(now) {
// 清除画布(透明,不遮挡聊天背景)
ctx.clearRect(0, 0, w, h);
// 更新并绘制存活粒子
particles = particles.filter((p) => p.alpha > 0.02);
particles.forEach((p) => {
p.update();
p.draw(ctx);
});
if (now - startTime < DURATION) {
animId = requestAnimationFrame(animate);
} else {
clearInterval(launchInterval);
cancelAnimationFrame(animId);
ctx.clearRect(0, 0, w, h);
onEnd();
}
}
animId = requestAnimationFrame(animate);
}
return { start };
})();