150 lines
4.9 KiB
JavaScript
150 lines
4.9 KiB
JavaScript
/**
|
||
* 文件功能:聊天室特效管理器
|
||
*
|
||
* 统一管理全屏 Canvas 特效的入口、防重入和资源清理。
|
||
* 使用方式:EffectManager.play('fireworks' | 'rain' | 'lightning' | 'snow' | 'sakura' | 'meteors' | 'gold-rain' | 'hearts' | 'confetti' | 'fireflies')
|
||
*/
|
||
|
||
const EffectManager = (() => {
|
||
// 当前正在播放的特效名称(防止同时播放两个特效)
|
||
let _current = null;
|
||
// 全屏 Canvas 元素引用
|
||
let _canvas = null;
|
||
// 待播放特效队列,避免多个进场效果互相打断
|
||
const _queue = [];
|
||
|
||
/**
|
||
* 获取或创建全屏 Canvas 元素
|
||
* 属性:fixed 定位,覆盖全屏,pointer-events:none 不阻止用户交互
|
||
*/
|
||
function _getCanvas() {
|
||
if (_canvas && document.body.contains(_canvas)) {
|
||
return _canvas;
|
||
}
|
||
const c = document.createElement("canvas");
|
||
c.id = "effect-canvas";
|
||
c.style.cssText = [
|
||
"position:fixed",
|
||
"top:0",
|
||
"left:0",
|
||
"width:100vw",
|
||
"height:100vh",
|
||
"z-index:99999",
|
||
"pointer-events:none",
|
||
].join(";");
|
||
c.width = window.innerWidth;
|
||
c.height = window.innerHeight;
|
||
document.body.appendChild(c);
|
||
_canvas = c;
|
||
return c;
|
||
}
|
||
|
||
/**
|
||
* 特效结束后清理 Canvas,重置状态,并停止音效
|
||
*/
|
||
function _cleanup() {
|
||
if (_canvas && document.body.contains(_canvas)) {
|
||
document.body.removeChild(_canvas);
|
||
}
|
||
_canvas = null;
|
||
_current = null;
|
||
// 通知音效引擎停止(兜底:正常情况下音效会自行计时结束)
|
||
if (typeof EffectSounds !== "undefined") {
|
||
EffectSounds.stop();
|
||
}
|
||
|
||
if (_queue.length > 0) {
|
||
const nextType = _queue.shift();
|
||
if (nextType) {
|
||
play(nextType);
|
||
}
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 播放指定特效
|
||
*
|
||
* @param {string} type 特效类型:fireworks / rain / lightning / snow / sakura / meteors / gold-rain / hearts / confetti / fireflies
|
||
*/
|
||
function play(type) {
|
||
// 防重入:同时只允许一个特效
|
||
if (_current) {
|
||
console.log(`[EffectManager] 特效 ${_current} 正在播放,加入队列 ${type}`);
|
||
_queue.push(type);
|
||
return;
|
||
}
|
||
|
||
const canvas = _getCanvas();
|
||
_current = type;
|
||
|
||
// 同步触发对应音效
|
||
if (typeof EffectSounds !== "undefined") {
|
||
EffectSounds.play(type);
|
||
}
|
||
|
||
switch (type) {
|
||
case "fireworks":
|
||
if (typeof FireworksEffect !== "undefined") {
|
||
FireworksEffect.start(canvas, _cleanup);
|
||
}
|
||
break;
|
||
case "wedding-fireworks":
|
||
// 婚礼专属:双倍礼花,粉金浪漫配色,持续 12 秒
|
||
if (typeof FireworksEffect !== "undefined") {
|
||
FireworksEffect.startDouble(canvas, _cleanup);
|
||
}
|
||
break;
|
||
case "rain":
|
||
if (typeof RainEffect !== "undefined") {
|
||
RainEffect.start(canvas, _cleanup);
|
||
}
|
||
break;
|
||
case "lightning":
|
||
if (typeof LightningEffect !== "undefined") {
|
||
LightningEffect.start(canvas, _cleanup);
|
||
}
|
||
break;
|
||
case "snow":
|
||
if (typeof SnowEffect !== "undefined") {
|
||
SnowEffect.start(canvas, _cleanup);
|
||
}
|
||
break;
|
||
case "sakura":
|
||
if (typeof SakuraEffect !== "undefined") {
|
||
SakuraEffect.start(canvas, _cleanup);
|
||
}
|
||
break;
|
||
case "meteors":
|
||
if (typeof MeteorsEffect !== "undefined") {
|
||
MeteorsEffect.start(canvas, _cleanup);
|
||
}
|
||
break;
|
||
case "gold-rain":
|
||
if (typeof GoldRainEffect !== "undefined") {
|
||
GoldRainEffect.start(canvas, _cleanup);
|
||
}
|
||
break;
|
||
case "hearts":
|
||
if (typeof HeartsEffect !== "undefined") {
|
||
HeartsEffect.start(canvas, _cleanup);
|
||
}
|
||
break;
|
||
case "confetti":
|
||
if (typeof ConfettiEffect !== "undefined") {
|
||
ConfettiEffect.start(canvas, _cleanup);
|
||
}
|
||
break;
|
||
case "fireflies":
|
||
if (typeof FirefliesEffect !== "undefined") {
|
||
FirefliesEffect.start(canvas, _cleanup);
|
||
}
|
||
break;
|
||
default:
|
||
console.warn(`[EffectManager] 未知特效类型:${type}`);
|
||
_cleanup();
|
||
}
|
||
}
|
||
|
||
return { play };
|
||
})();
|