不能——iOS Safari 必须通过用户可信手势(如 touchstart)调用 DeviceMotionEvent.requestPermission() 后才能监听 devicemotion,且 WKWebView 中需原生配合,摇一摇检测需基于 accelerationIncludingGravity 的向量模长变化率双阈值判定。

iOS Safari 能否直接触发 DeviceMotion 事件?
不能——除非用户完成一次「可信的用户手势」(如点击、触摸、键盘输入)。这是 iOS 13+ 的硬性限制,不是 bug,是 Safari 的安全策略。直接在 window.onload 或 setTimeout 里监听 deviceorientation 或 devicemotion,iOS 会静默拒绝启用传感器,event.acceleration.x 等字段始终为 null。
必须用「用户点击」解锁传感器权限
只有在用户主动触发的事件回调中,才能首次调用 DeviceMotionEvent.requestPermission()(iOS 13+)或直接开始监听(iOS 12 及更早需手动唤醒)。否则摇一摇永远没响应。
- 必须绑定在
button、div等可点击元素的onclick或ontouchstart上(推荐后者,避免点击延迟) - 调用后需显式等待 Promise resolve,再启动监听:
document.getElementById('shake-btn').ontouchstart = async () => { try { await DeviceMotionEvent.requestPermission(); window.addEventListener('devicemotion', handleShake); } catch (e) { console.warn('Permission denied:', e); } }; - ⚠️ 注意:Safari 不支持在 iframe 中请求权限(尤其跨域 iframe),必须在顶层页面执行
如何可靠检测「摇一摇」动作?
别依赖单次加速度峰值——iOS 设备休眠时传感器数据抖动大,且不同机型 baseline 不同。应计算加速度向量模长变化率,并设双阈值过滤噪声。
- 用
event.accelerationIncludingGravity(非acceleration),它包含重力分量,更稳定 - 每帧计算合加速度:
const acc = Math.sqrt(x*x + y*y + z*z) - 只在连续 3 帧内
acc > 25(单位 m/s²)且与前一稳定值差值 > 12 时才判定为有效摇动 - 摇动后需重置参考基线,避免连续误触发
WKWebView 中无法调用 requestPermission 怎么办?
WKWebView 默认禁用传感器 API,即使网页里写了 requestPermission 也会抛 SecurityError。必须由原生层显式开启:
立即学习“前端免费学习笔记(深入)”;
- iOS 14.5+:在
WKWebViewConfiguration中设置mediaTypesRequiringUserActionForPlayback = []不够,还需configuration.preferences.setValue(true, forKey: "allowAirPlayForMediaPlayback")——但这不解决传感器问题 - 真正解法:原生注入
WKScriptMessageHandler,当 JS 触发「准备摇一摇」时,OC/Swift 调用[self.webView evaluateJavaScript:@"..." completionHandler:nil]并提前配置好configuration.usesWebProcessPool = NO(不推荐)或改用SFSafariViewController - 更实际的方案:放弃 WKWebView 内摇一摇,改用原生
CMMotionManager检测,JS 通过webkit.messageHandlers.xxx.postMessage()接收事件










