移动端音频播放需用户手势触发,iOS/Android均限制自动播放;首次交互须静音预播放解锁;iOS播放后需load()重置状态;拖拽需自实现touch逻辑;MP3优先换AAC并确保CBR编码。

play() 被拒绝:iOS 和 Android 的自动播放限制必须绕过
移动端网页调用 audio.play() 报错 Uncaught (in promise) DOMException: play() can only be initiated by a user gesture,不是你代码写错了,是浏览器强制策略。iOS Safari 和 Chrome on Android 都要求首次音频播放必须由用户真实交互(如 touchstart、click)触发,否则 Promise 直接 reject。
- 不能在
DOMContentLoaded或 AJAX 回调里直接play(),哪怕 audio 已load() - “静音预播放”是最通用的解锁方案:首次交互时创建一个
muted的Audio实例,调一次play().then(() => pause()),之后所有音频就能正常播放 - 注意:Android WebView 有些版本(尤其旧版系统内嵌 WebView)可能不认
touchstart,可 fallback 到click或监听document.body的ontouchend
播完一遍就失效:iOS 上 audio 只能 play 一次?
iOS 的 HTMLMediaElement 在播放结束后进入 ended 状态,再次调用 play() 会失败,报错或静默失败——这不是 bug,是设计行为。关键在于:状态没重置,播放器就“卡住”了。
- 每次重新播放前,必须先调用
load()(清空当前状态并重载元数据),再play() - 更稳妥的做法是复用同一个
Audio实例,但每次播放前重置:currentTime = 0+load()+play() - 避免频繁新建
new Audio(src),容易触发内存泄漏或资源竞争,尤其在快速连点场景下
进度条拖拽失灵:移动端 touch 事件没接管好
原生 在 iOS 上拖动卡顿、响应延迟,Android 部分机型甚至不触发 change;而直接监听 audio.currentTime 并手动设置,在触摸过程中极易因异步渲染导致跳变或卡死。
- 必须自己实现拖拽逻辑:监听
touchstart→ 记录初始位置 →touchmove实时计算偏移 →touchend后设置currentTime - 记得在
touchmove中调用event.preventDefault(),防止页面滚动干扰 - 不要依赖
timeupdate事件实时更新进度条宽度,它频率低且不可靠;应根据currentTime / duration主动计算并更新 DOM
格式与编码踩坑:MP3 在 iOS 上突然不响?
不是所有 MP3 都能在 iOS 上播。Safari 对音频文件有隐性要求:必须是 CBRS(Constant Bit Rate)编码,且 ID3 标签不能损坏或含非法字段。很多用 Audacity 或在线工具导出的“MP3”,实际是 VBR 编码或带乱码封面,iOS 就直接静音或报错加载失败。
- 优先用 AAC(.m4a)替代 MP3,iOS 原生支持更好,体积更小,解码更省电
- 用
ffprobe -v quiet -show_entries format=bit_rate:stream=codec_name,bits_per_raw_sample -of default=nw=1检查是否为 CBR - 微信内嵌 WebView 更极端:某些安卓微信版本会拦截非 HTTPS 的 audio src,哪怕本地 file:// 也不行,务必走 HTTPS










