Files
chatroom/public/js/effects/effect-manager.js
lkddi 1d7aa636a0 功能:4种全屏特效增加 Web Audio API 实时合成音效
新建 public/js/effects/effect-sounds.js:
- 雷电:低频白噪声爆裂 + 雷鸣渐衰(10次,与视觉同步)
- 烟花:发射滑音(200→700Hz)+ 带通噪声爆炸(9轮)
- 下雨:双层带通白噪声(1200Hz+3500Hz)持续淡入淡出
- 下雪:4000Hz+高频风声 + 五声音阶轻柔铃音(5次随机)
- 所有音效纯 Web Audio API 合成,无外部音频文件
- 旧 AudioContext 若被 suspended 自动 resume

effect-manager.js:
- play() 调用 EffectSounds.play(type) 同步触发音效
- _cleanup() 调用 EffectSounds.stop() 兜底停止

frame.blade.php:effect-sounds.js 在 effect-manager 前引入
2026-03-01 13:07:36 +08:00

106 lines
3.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 特效的入口、防重入和资源清理。
* 使用方式EffectManager.play('fireworks' | 'rain' | 'lightning')
*/
const EffectManager = (() => {
// 当前正在播放的特效名称(防止同时播放两个特效)
let _current = null;
// 全屏 Canvas 元素引用
let _canvas = null;
/**
* 获取或创建全屏 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();
}
}
/**
* 播放指定特效
*
* @param {string} type 特效类型fireworks / rain / lightning / snow
*/
function play(type) {
// 防重入:同时只允许一个特效
if (_current) {
console.log(
`[EffectManager] 特效 ${_current} 正在播放,忽略 ${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 "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;
default:
console.warn(`[EffectManager] 未知特效类型:${type}`);
_cleanup();
}
}
return { play };
})();