摇一摇必须用 DeviceMotionEvent API 而非 deviceorientation,因前者检测加速度突变(x/y/z三轴模长变化率),后者仅监听朝向角度且跨端不可靠;需在 HTTPS 下运行,通过 accelerationIncludingGravity 计算模长并设阈值(如20)和差值(如15)识别有效冲击。

HTML 本身做不到摇一摇,必须靠 JavaScript 调用设备的 DeviceMotionEvent API —— 而且只在 HTTPS 环境下生效,HTTP 页面会直接静默失败。
为什么 deviceorientation 不适合做摇一摇
很多人搜到 deviceorientation 就直接用,结果发现:手机转个方向就触发,但“摇”动作反而很难捕捉。因为它是监听设备朝向(alpha/beta/gamma),不是加速度变化。DeviceMotionEvent 才是正解,它提供 accelerationIncludingGravity 和 rotationRate,能真实反映「突然加速→减速」这个摇的物理特征。
- 摇一摇本质是检测短时间内 x/y/z 三轴加速度的**突变幅度**,不是角度偏移
-
deviceorientation在 iOS Safari 上默认被禁用(需用户手势激活),而devicemotion在用户交互后可启用 - 安卓 Chrome 对
deviceorientation的权限策略更松,但行为不一致,跨端不可靠
怎么用 DeviceMotionEvent 检测一次有效摇动
核心是监听 devicemotion 事件,计算加速度模长变化率,过滤掉缓慢移动和持续抖动。关键参数不是“绝对值”,而是“变化量”:
- 每次事件取
event.accelerationIncludingGravity.x/y/z(注意:iOS 返回的是带重力分量的值,更稳定) - 用欧氏距离算当前加速度模长:
Math.sqrt(x*x + y*y + z*z) - 只在模长**超过阈值(如 20)且与上一次值差值 > 15** 时记为一次“冲击”
- 连续两次冲击间隔
示例片段(无框架依赖):
let lastAcc = 0;
let shakeStart = 0;
let shakeCount = 0;
<p>function handleMotion(e) {
const { x, y, z } = e.accelerationIncludingGravity || {};
if (x === null) return;
const acc = Math.sqrt(x<em>x + y</em>y + z*z);</p><p>if (acc > 20 && Math.abs(acc - lastAcc) > 15) {
const now = Date.now();
if (!shakeStart) shakeStart = now;
if (now - shakeStart </p><p>function resetShake() {
shakeCount = 0;
shakeStart = 0;
}</p><p>window.addEventListener('devicemotion', handleMotion);</p>
常见失败原因和绕过方案
即使代码写对,90% 的失败来自环境限制或权限问题:
-
devicemotion在 iOS 13+ 必须由用户手势(如click、touchstart)触发后才能监听,否则事件完全不触发 —— 解决办法:首次点击按钮时调用requestPermission()(仅 iOS) - HTTP 页面(非 localhost)下,所有 motion/orientation API 被浏览器直接禁用 —— 没有绕过,必须部署到 HTTPS
- 部分安卓 WebView(如微信内置)禁用该 API,可降级检测
navigator.vibrate是否存在,不存在则提示“请在 Safari 或 Chrome 中打开” - 模拟器无法触发摇一摇 —— 真机调试是唯一验证方式,Chrome DevTools 的 Sensors 面板仅模拟
deviceorientation,不支持devicemotion
真正难的不是写几行监听代码,而是处理 iOS 权限弹窗时机、判断是否在 WebView 里、以及如何优雅地降级提示 —— 这些细节没覆盖,功能在真实用户手里基本等于失效。
前端入门到VUE实战笔记:立即使用
在学习笔记中,你将探索 前端 的入门与实战技巧!











