navigator.vibrate() 必须由用户手势同步调用且仅安卓部分支持,iOS 全面禁用;需配合视觉反馈(如按钮缩放、抖动动画),并做 API 检测与降级处理。

直接用 navigator.vibrate() 会静默失败
HTML 标签页切换本身不触发震动,navigator.vibrate() 也不会自动绑定到 tab 切换行为上。它必须由用户手势(如 click 或 touchstart)**同步调用**,且首次调用必须发生在用户真实交互的事件处理函数内部。放在 setTimeout、transitionend 回调或状态更新后异步执行,都会返回 true 却无震动——iOS 尤其严格,连后台标签页切回来都要重新手势激活一次。
安卓能震,iOS 基本不支持
别指望 iOS Safari 能提供震动反馈:navigator.vibrate() 在所有已知 iOS 版本中均被禁用,调用既不报错也不生效。Android Chrome 和 WebView 支持单值(如 100)和短数组(如 [50, 30, 50]),但 MIUI、EMUI 等定制系统常屏蔽该 API 或限制总时长(常见上限 1000ms)。实测可靠参数是 80–120ms 单次短震,避免用 0(清队列语义,部分机型无感)。
必须搭配视觉反馈,否则用户根本感知不到
震动只是增强层,不是主反馈通道。用户点标签没看到按钮缩放、没看到面板淡入,就不会意识到“我点成功了”,更不会察觉是否震了。尤其在静音/请勿打扰/触感关闭场景下,震动必然失效。
- 给 tab 按钮加
:active { transform: scale(0.96); }和背景色加深 - 确保
cursor: pointer已设置,否则 iOS Safari 不触发:active - 标签面板切换用
opacity+transform: translateY()组合过渡,不要只靠 opacity - 震动调用前检查:
'vibrate' in navigator && typeof navigator.vibrate === 'function',调用后判断返回值是否为false来降级
真正可行的写法:在 touchstart/click 中触发,且仅限明确语义动作
把震动逻辑塞进标签按钮的 touchstart(比 click 延迟更低)里,但只对有明确物理意义的操作启用,比如“提交表单后切换结果页”“错误确认弹窗关闭后跳回首页”,而不是每次 tab 切换都震——高频震动会让用户反感,也易被系统节流。
将 Markdown 文章转换为微信公众号兼容的纯 HTML 格式。当用户要求"转成微信格式"、"生成公众号文章 HTML"、"排版到微信草稿箱"、"微信粘贴格式"时触发。输出带内联样式的纯 HTML,微信编辑器可直接渲染,无需额外适配。支持标题、段落、列表、粗体、行内代码、表格、引用块、代码块、配图的完整转换。
立即学习“前端免费学习笔记(深入)”;
tabBtn.addEventListener('touchstart', function(e) {
e.preventDefault(); // 防止部分安卓浏览器默认高亮干扰
if ('vibrate' in navigator && typeof navigator.vibrate === 'function') {
const ok = navigator.vibrate(100);
if (!ok) {
// 触发视觉 fallback:按钮微抖 + 图标变色
this.classList.add('shake');
setTimeout(() => this.classList.remove('shake'), 400);
}
}
});
注意:抖动动画要用 transform 实现(非 margin 或 left),并配 will-change: transform 和 overflow: hidden 父容器,否则低端设备易卡顿或元素溢出。
最易被忽略的一点:震动不是 UI 动效的一部分,它没有 DOM 生命周期。你无法监听“震动结束”,也不能把它和 CSS transition 对齐。把它当成一次独立的、不可靠的硬件请求,永远以视觉反馈为第一优先级。


















