navigator.vibrate() 必须在用户手势事件(如 click、pointerdown)中同步调用,参数为数字或偶数长度数字数组且总时长≤5000ms;iOS 限制极严,仅 HTTPS 页面在开启触感反馈且首次合法调用后支持≤15ms 单次震动。

直接传数字或数组就能触发,但绝大多数“没反应”不是代码写错,而是被浏览器拦截或设备不认——navigator.vibrate() 从来就不是“写了就震”的 API。
必须在用户手势事件里同步调用
这是最常踩的坑:把 navigator.vibrate() 放在 setTimeout、Promise.then、fetch 回调或页面加载完成时执行,一律静默失败(不报错,也不震)。
- ✅ 正确做法:绑定到
click、touchstart或pointerdown的事件处理器中,且调用必须是同步的(不能 await、不能延迟) - ⚠️
pointerdown比click更可靠:兼容触摸和鼠标,触发时机更早,避免因防抖/跳转中断震动 - ❌ 即使事件由点击发起,若震动逻辑藏在异步链里(比如按钮点击 → 触发 API → 成功后
then里调用),也会失效
参数只能是数字或偶数长度数字数组
navigator.vibrate() 对输入极其敏感,类型或结构不对就会被静默忽略(返回 true 但无震动)。
- ✅ 有效参数:
navigator.vibrate(150)(单次 150ms)、navigator.vibrate([200, 80, 150])(震 200ms → 停 80ms → 震 150ms) - ❌ 无效参数:
navigator.vibrate("150")(字符串)、navigator.vibrate([100, 200, 300])(奇数长度,只执行前两项)、navigator.vibrate([0, 100])(部分 Android 截断为[100]或直接丢弃) - ⚠️ 数组总时长不能超 5000ms;项数建议 ≤ 5,否则部分 WebView 可能截断或报
InvalidStateError
iOS Safari 的限制比你想象中更硬
iOS 不是“支持差”,而是“几乎不支持”——即使系统是 iOS 16.4+,也需同时满足三个条件才可能震一次:
立即学习“前端免费学习笔记(深入)”;
- 用户已开启「辅助功能 → 触感反馈」
- 页面是 HTTPS
- 此前已有至少一次合法手势触发过
vibrate(首次调用必须在click等事件中) - 且只能传单个数字 ≤ 15ms(官方文档写 ≤ 500ms,实测 ≥ 16ms 就大概率被系统截断或静默忽略)
所以别传 [100, 50, 100] 或 200 给 iOS,它只会返回 false 或静默失败。真要保底,统一用 navigator.vibrate(15) 并接受它“有时不响”。
震动不是独立反馈,得配降级和节流
靠震动传递关键信息等于埋雷:iOS 不响、部分安卓厂商屏蔽、用户开了“减少动态效果”、后台标签页……这些都会让震动消失。
- ✅ 必须搭配视觉反馈:按钮点击时加
scale(0.98)+ 背景色瞬变,哪怕震动失效也能感知 - ✅ 节流很关键:连点按钮时快速多次调用
vibrate(),Android 会排队震个不停,iOS 可能卡住。每次调用前先清队列:navigator.vibrate(0),再加 300ms 最小间隔 - ✅ 检测无障碍偏好:
matchMedia('(prefers-reduced-motion: reduce)').matches为真时,直接跳过震动
真正难的不是怎么写那行 navigator.vibrate(),而是怎么让它在各种“不配合”的设备上,依然不破坏交互节奏——这需要你主动放弃控制权,把震动当成锦上添花,而不是操作闭环的一部分。



















