必须调用uni.startAccelerometer()启动传感器,否则uni.onAccelerometerChange无法触发;需用加速度变化率(jerk)判定摇一摇,缓存3–5帧计算差值平方和连续超阈值才响应,并在onHide/onUnload中配对注销监听与停止传感器。

必须调用 uni.startAccelerometer() 才能触发监听,否则 uni.onAccelerometerChange 永远收不到数据——这是 90% 的“没反应”问题的根源。
为什么 uni.onAccelerometerChange 注册了却没回调
不是监听没写对,而是传感器根本没启动。uni-app 的加速度监听是“两步走”:先 uni.startAccelerometer() 开硬件,再 uni.onAccelerometerChange(callback) 接数据。漏掉第一步,回调函数就是摆设。
- Android 和 iOS 都要求显式启动,H5 端压根不支持该系列 API,模拟器也无效
- iOS 真机首次调用必须由用户手势触发(如点击按钮),页面 onLoad 自动调用会静默失败,无报错
- manifest.json 中 Android 要确保勾选「加速度传感器」权限;iOS 还需在
Info.plist补充NSMotionUsageDescription字符串,否则权限弹窗都不出现 - 别混淆
uni.getAccelerometerFrequency:它只是设置采样频率(如"ui"或"game"),不启动监听,也不返回数据
怎么判断是“摇一摇”,而不是走路或放手机
单看 Math.abs(res.x) > 2.5 这类单轴阈值,在真机上必然误判——躺着摇、侧握、边走边摇都会漏;按屏幕、抖手腕又容易误触。核心得盯住“加速度变化率(jerk)”,即单位时间内的加速度突变。
- 每帧计算合加速度:
const acc = Math.sqrt(x*x + y*y + z*z) - 缓存最近 3–5 帧的
acc值(用accHistory.push(acc).slice(-5)) - 只当连续 2–3 帧的差值平方和
(dx*dx + dy*dy + dz*dz)> 20 时,才记为一次有效抖动 - 累计 2–3 次抖动后才触发动作,比单次超阈值更稳
- 避免开方性能损耗:差值平方和比模长变化率更轻量,实测效果一致
防抖、锁定期与生命周期清理必须配对
一次真实摇动常触发多个峰值,不控就会连弹三次弹窗、连发三次请求。同时,监听未注销会导致页面跳走后还在执行回调,轻则报错 Cannot read property 'xxx' of null,重则持续耗电。
- 检测到有效抖动后,立即设
isShaking = true,并用setTimeout在 1500ms 后重置,期间所有新抖动都忽略 - 不要用
clearTimeout动态重置定时器——摇动持续时间不可控,固定窗口更可靠 - 必须在
onHide或onUnload中调用uni.offAccelerometerChange(handle),且传入的回调引用要和注册时完全一致 - 推荐封装句柄:
const handle = res => { /* 处理逻辑 */ };→uni.onAccelerometerChange(handle)→uni.offAccelerometerChange(handle) - 页面不可见时(
onHide)应顺带调用uni.stopAccelerometer(),省电且防后台被系统休眠中断
平台差异和容易被忽略的细节
Android 小米/华为省电模式可能限制后台采样;iOS WKWebView 对首次交互极其敏感;而所有平台都不会告诉你“为什么没数据”——失败是静默的。
- Android 8.0+ 和多数国产 ROM 不允许 App.vue 的
onLaunch全局启动传感器,必须推迟到具体页面的onShow - iOS 上若用户从未点过“开启摇一摇”按钮,
uni.onAccelerometerChange永远不会执行,控制台零提示 - 别在 H5 端白费力气适配——它不支持
uni.onAccelerometerChange,要用window.addEventListener('devicemotion', ...),但需 WebView 显式透传权限(Cordova/Capacitor 额外配置) - 测试务必用真机:iOS 和 Android 设备行为差异大,阈值需分别调优,比如 iOS 建议模长峰值 > 18 m/s² 且 jerk > 30 m/s³


















