HTML5 Audio元素不支持淡入淡出,需用Web Audio API通过GainNode的setValueAtTime和linearRampToValueAtTime实现平滑音量过渡,注意iOS限制、时间精度及资源释放。

HTML5 Audio 元素本身不支持淡入淡出
浏览器原生 <audio> 标签没有 fadeIn() 或 fadeOut() 方法,直接改 volume 会卡顿、不平滑,且在 iOS Safari 上常被静音策略拦截——这不是你代码写错了,是 API 限制。
真正能做淡入淡出的,只有 Web Audio API。它绕过 HTML 音频的播放限制,允许逐帧控制增益(gain),从而实现线性或缓动音量变化。
用 Web Audio API 实现淡入:关键三步
核心是把 <audio> 挂载到 AudioContext,再通过 GainNode 调节音量。不是“替换音频”,而是“接管音频输出”。
- 先创建
AudioContext(注意:必须用户手势触发,比如click事件里初始化) - 用
ctx.createMediaElementSource(audioEl)把 HTML<audio>接入音频图 - 接一个
GainNode,调用gain.setValueAtTime()和gain.linearRampToValueAtTime()控制过渡
示例片段(淡入 2 秒):
立即学习“前端免费学习笔记(深入)”;
const audio = document.querySelector('audio');
const ctx = new (window.AudioContext || window.webkitAudioContext)();
const source = ctx.createMediaElementSource(audio);
const gainNode = ctx.createGain();
source.connect(gainNode);
gainNode.connect(ctx.destination);
// 淡入:从 0 → 1,耗时 2 秒
gainNode.gain.setValueAtTime(0, ctx.currentTime);
gainNode.gain.linearRampToValueAtTime(1, ctx.currentTime + 2);
淡出时 pause() 和 stop() 的区别很关键
淡出结束要不要停播放?别直接 audio.pause() —— 它只暂停元素,但 Web Audio 图仍在运行,可能继续消耗资源;也别手动清空 gain,容易漏掉时间参数导致跳变。
- 正确做法:在淡出终点用
gain.setValueAtTime(0)彻底静音,再调用audio.pause()和audio.currentTime = 0(如需重播) - 如果想彻底释放资源,记得
source.disconnect()、gainNode.disconnect(),尤其在单页应用中反复创建音频时 - iOS Safari 下,淡出后立即
play()新音频大概率失败——必须等上一个audio的ended事件或显式load()
linearRampToValueAtTime 不是万能的,注意时间精度陷阱
Web Audio 时间戳基于高精度单调时钟(ctx.currentTime),但如果你用 setTimeout 或 requestAnimationFrame 去“模拟”淡入,误差会累积,尤其在后台标签页或低性能设备上。
- 永远用
ctx.currentTime计算起始/终点时间,不要用Date.now()或performance.now() - 避免链式多次
linearRampToValueAtTime—— 它只能定义两点间直线,想做缓动(ease-in-out)得用exponentialRampToValueAtTime或手动插值 +setValueAtTime - 音量不能设为负数,也不能超过 1(除非你刻意过载),
gain.value = 1.5是合法的,但人耳感知非线性,建议淡入上限设为0.95更安全
复杂点在于:淡入淡出要和音频实际播放位置对齐,而 audio.currentTime 和 ctx.currentTime 并不同步——得靠 audio 的 timeupdate 事件定期校准,或者干脆只用 Web Audio 驱动播放(用 AudioBufferSourceNode),但这意味着放弃 <audio> 的流式加载和进度条原生支持。











