功能:大卡片/小卡片弹出时播放叮咚通知音
effect-sounds.js:
- 新增 ding() 函数:A5(880Hz) + E5(659Hz) 两音叮咚
每音含基音×2.76铃铛泛音,快冲击+铃铛式衰减
自动检查 chat_sound_muted 禁音标志
- 导出 ding 至返回对象,底部暴露 window.chatSound = {ding}
toast-notification.blade.php:
- chatToast.show() 中 appendChild 后调用 window.chatSound.ding()
scripts.blade.php:
- chatBanner.show() 开头调用 window.chatSound.ding()
This commit is contained in:
@@ -6,6 +6,7 @@
|
||||
* 对外 API:
|
||||
* EffectSounds.play(type) 播放指定特效的背景音效
|
||||
* EffectSounds.stop() 停止并释放当前音效资源
|
||||
* EffectSounds.ding() 播放简短叮咚通知音(大/小卡片弹出时使用)
|
||||
*
|
||||
* 支持的 type:
|
||||
* lightning 雷鸣闪电(三层合成:放电啪声 + 低频轰鸣 + 极低频滚动余韵)
|
||||
@@ -428,6 +429,72 @@ const EffectSounds = (() => {
|
||||
}
|
||||
}
|
||||
|
||||
// ─── 叮咚通知音(大卡片 / 小卡片弹出时调用)─────────────────
|
||||
|
||||
/**
|
||||
* 播放简短的叮咚两音通知音效。
|
||||
*
|
||||
* 音型:A5(880Hz)→ 间隔 110ms → E5(659Hz),均为正弦波,
|
||||
* 快速冲击 + 铃铛式缓慢衰减,总时长约 0.5 秒。
|
||||
* 禁音状态下自动跳过。
|
||||
*/
|
||||
function ding() {
|
||||
if (localStorage.getItem("chat_sound_muted") === "1") return;
|
||||
try {
|
||||
const ctx = _getCtx();
|
||||
const master = ctx.createGain();
|
||||
master.gain.value = 0.45;
|
||||
master.connect(ctx.destination);
|
||||
|
||||
/**
|
||||
* 播放单音:快速冲击 + 铃铛衰减包络
|
||||
*
|
||||
* @param {number} freq 频率(Hz)
|
||||
* @param {number} t0 相对于 ctx.currentTime 的时刻
|
||||
* @param {number} decay 衰减时长(秒)
|
||||
*/
|
||||
function _tone(freq, t0, decay) {
|
||||
const osc = ctx.createOscillator();
|
||||
osc.type = "sine";
|
||||
osc.frequency.value = freq;
|
||||
|
||||
// 轻柔泛音(×2.76 铃铛泛音比,音量 10%)
|
||||
const osc2 = ctx.createOscillator();
|
||||
osc2.type = "sine";
|
||||
osc2.frequency.value = freq * 2.76;
|
||||
const g2 = ctx.createGain();
|
||||
g2.gain.value = 0.1;
|
||||
|
||||
const env = ctx.createGain();
|
||||
env.gain.setValueAtTime(1.0, t0);
|
||||
env.gain.exponentialRampToValueAtTime(0.001, t0 + decay);
|
||||
|
||||
osc.connect(env);
|
||||
osc2.connect(g2);
|
||||
g2.connect(env);
|
||||
env.connect(master);
|
||||
|
||||
osc.start(t0);
|
||||
osc.stop(t0 + decay + 0.05);
|
||||
osc2.start(t0);
|
||||
osc2.stop(t0 + decay + 0.05);
|
||||
}
|
||||
|
||||
const now = ctx.currentTime;
|
||||
_tone(880, now, 0.35); // 叮:A5,先响
|
||||
_tone(659, now + 0.11, 0.4); // 咚:E5,稍低稍长
|
||||
|
||||
// 0.7 秒后释放主节点
|
||||
setTimeout(() => {
|
||||
try {
|
||||
master.disconnect();
|
||||
} catch (_) {}
|
||||
}, 700);
|
||||
} catch (e) {
|
||||
console.warn("[EffectSounds.ding] 通知音播放失败:", e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 停止当前音效并释放资源。
|
||||
*/
|
||||
@@ -440,5 +507,8 @@ const EffectSounds = (() => {
|
||||
}
|
||||
}
|
||||
|
||||
return { play, stop };
|
||||
return { play, stop, ding };
|
||||
})();
|
||||
|
||||
// 将叮咚通知音暴露为独立全局变量,供 toast/banner 等组件直接调用
|
||||
window.chatSound = { ding: () => EffectSounds.ding() };
|
||||
|
||||
@@ -822,6 +822,9 @@
|
||||
function show(opts = {}) {
|
||||
ensureKeyframes();
|
||||
|
||||
// 大卡片弹出时播放叮咚通知音
|
||||
if (window.chatSound) window.chatSound.ding();
|
||||
|
||||
const id = opts.id || 'chat-banner-default';
|
||||
const gradient = (opts.gradient || ['#4f46e5', '#7c3aed', '#db2777']).join(', ');
|
||||
const titleColor = opts.titleColor || '#fde68a';
|
||||
|
||||
@@ -105,6 +105,9 @@
|
||||
|
||||
container.appendChild(card);
|
||||
|
||||
// 弹出时播放叮咚通知音
|
||||
if (window.chatSound) window.chatSound.ding();
|
||||
|
||||
// 自动消失
|
||||
if (duration > 0) {
|
||||
setTimeout(() => dismiss(card), duration);
|
||||
|
||||
Reference in New Issue
Block a user