功能:大卡片/小卡片弹出时播放叮咚通知音

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:
2026-03-01 13:28:19 +08:00
parent dac7750fe1
commit 58b63fa8d3
3 changed files with 77 additions and 1 deletions

View File

@@ -6,6 +6,7 @@
* 对外 API
* EffectSounds.play(type) 播放指定特效的背景音效
* EffectSounds.stop() 停止并释放当前音效资源
* EffectSounds.ding() 播放简短叮咚通知音(大/小卡片弹出时使用)
*
* 支持的 type
* lightning 雷鸣闪电(三层合成:放电啪声 + 低频轰鸣 + 极低频滚动余韵)
@@ -428,6 +429,72 @@ const EffectSounds = (() => {
}
}
// ─── 叮咚通知音(大卡片 / 小卡片弹出时调用)─────────────────
/**
* 播放简短的叮咚两音通知音效。
*
* 音型A5880Hz→ 间隔 110ms → E5659Hz均为正弦波
* 快速冲击 + 铃铛式缓慢衰减,总时长约 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() };

View File

@@ -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';

View File

@@ -105,6 +105,9 @@
container.appendChild(card);
// 弹出时播放叮咚通知音
if (window.chatSound) window.chatSound.ding();
// 自动消失
if (duration > 0) {
setTimeout(() => dismiss(card), duration);