震动需先检测navigator.vibrate是否可用且为函数,仅用户手势触发才生效;参数推荐单数字如150ms;iOS完全不支持,须降级视觉反馈。

震动不生效?先确认 navigator.vibrate 是否可用
不是所有浏览器或设备都支持 navigator.vibrate,它在 iOS Safari 上完全不可用(包括所有 WKWebView 场景),Android Chrome、Firefox 和 Edge(基于 Chromium)支持较好,但需满足「用户手势触发」这一硬性前提。
直接调用会静默失败,不会报错,所以必须先检测:
if ('vibrate' in navigator && typeof navigator.vibrate === 'function') {
// 可以安全使用
} else {
// 降级:改用 CSS 动画/音效/Toast 提示
}
- 仅检测
'vibrate' in navigator不够,iOS Safari 也返回true,但实际调用无效 —— 必须加typeof navigator.vibrate === 'function' - 即使检测通过,首次调用也必须由用户真实点击/触摸事件触发(如
onclick、ontouchend),不能放在setTimeout或异步回调里 - 后台标签页或页面被切换时,震动会被系统静默丢弃
震动参数怎么写才有效:毫秒数组 vs 单个数字
navigator.vibrate() 接收两种格式:单个数字(如 200)表示震动 200ms;数组(如 [200, 100, 300])表示「震 200ms → 停 100ms → 震 300ms」。但多数 Android 系统对数组长度和总时长有限制(常见上限为 5 项、总时长 ≤ 5000ms),超限会直接忽略整个调用。
- 简单反馈推荐用单数字:
navigator.vibrate(150)—— 兼容性最好,响应快,用户感知清晰 - 避免写
[100, 50, 100]这类短间隔组合,部分低端 Android 设备无法解析停顿,会合并成一次长震 - 不要传
0或负数,虽不报错,但无效果;传空数组[]或0会停止当前震动(但通常没必要主动停) - 同一时刻多次调用会覆盖前一次,无需手动防抖,但频繁触发(如连点)可能被系统节流
关键操作中如何安全集成震动反馈
震动必须嵌入用户可感知的交互闭环中,比如表单提交成功、开关切换完成、扫码识别命中。不能脱离上下文单独震动,否则用户会困惑「震什么?」
button.addEventListener('click', () => {
// 1. 先执行业务逻辑(如 API 调用)
api.submitForm().then(() => {
// 2. 仅在此处触发震动(且确保在同一个事件循环内)
if (navigator.vibrate) navigator.vibrate(150);
showSuccessToast();
});
});
- 别在 Promise
.catch()里震动 —— 错误反馈更适合用视觉/声音,震动易被理解为「操作成功」 - 如果操作本身是异步的(如等待扫码结果),需把震动放在明确的结果回调里,而不是监听开始事件
- 避免在
input或change事件中震动(如 checkbox 切换瞬间),因用户可能未完成操作(如误触),应等确认动作(如点击「确定」按钮)再触发 - 考虑无障碍:震动不是唯一反馈方式,务必搭配视觉变化(颜色/图标/文字)和可读的 ARIA 消息
iOS 用户完全收不到震动?这是设计使然,不是 bug
iOS 系统层禁止网页调用硬件震动,无论你检测多严密、参数多标准,在 Safari 或任何基于 WebKit 的 WebView 中,navigator.vibrate() 都不会产生物理反馈。这不是兼容性问题,而是苹果的明确限制。
- 不要尝试用
AudioContext播放低频音效模拟震动 —— 效果差、耗电、且 iOS 仍需用户手势激活音频,不可靠 - 对 iOS 用户,震动逻辑应直接跳过,转而强化视觉反馈:比如按钮按压缩放 + 微动动画 + 状态色块脉冲(用
@keyframes实现) - 如果项目强依赖触觉反馈(如工业扫码场景),需引导用户使用 PWA 安装后,部分 Android PWA 可获得更完整权限,但 iOS 依然无解
真正难处理的不是「怎么震」,而是「什么时候不该震」—— 用户没意识到自己开了震动,连续操作下容易疲劳;静音模式下震动反而突兀;弱网场景里业务逻辑还没跑完,震动先响了,会造成「假成功」错觉。这些细节比语法更重要。

















