uni.onAccelerometerChange需配合uni.startAccelerometer启用,否则静默失效;须在用户交互后调用并检查权限;抖动检测应基于合加速度突变而非单轴阈值;页面卸载时必须用具名函数注销监听;H5和小程序端不支持该API。

uni.onAccelerometerChange 必须配合 uni.startAccelerometer 才能生效
监听加速度计数据不是注册个回调就完事——uni.onAccelerometerChange 本身不启动传感器,它只是“等着收数据”。如果没调 uni.startAccelerometer(),回调永远不会触发,控制台也无报错,纯静默失败。
实操建议:
- 在页面
onShow或用户明确交互(如点击「开启体感」按钮)后调用uni.startAccelerometer(),避免冷启动时被系统拒绝或后台休眠中断 - Android 8.0+ 和多数国产 ROM 不允许 App 启动即后台采集,全局放在
App.vue的onLaunch里基本无效 - iOS WKWebView 要求首次传感器访问必须由用户手势触发(比如 tap),否则即使调了
startAccelerometer也收不到数据 - 务必检查
manifest.json:App 端需勾选「加速度传感器」权限;iOS 还要在Info.plist补充NSMotionUsageDescription字符串描述用途
别直接比 res.x/res.y/res.z 的绝对值
写 Math.abs(res.x) > 2.5 这类单轴阈值判断,在真机上几乎必然误判:走路颠簸、放手机、按屏幕都可能瞬时超限;而侧握、躺着摇又可能全轴都不达标。摇一摇的本质是“加速度突变”,也就是 jerk(加速度变化率)。
实操建议:
- 每帧计算合加速度:
const acc = Math.sqrt(res.x * res.x + res.y * res.y + res.z * res.z) - 缓存最近 3–5 帧的
acc值(用Array.push().slice(-5)控制长度) - 算差值平方和代替开方:
dx*dx + dy*dy + dz*dz(性能更好),连续 2–3 帧 > 20 才记为一次有效抖动 - 双阈值更稳:当前合加速度 > 18(≈1.8g)且与上一帧差值 > 1.8(单位 g),同时满足才响应
页面卸载时必须手动清理监听器
uni.onAccelerometerChange 是全局监听,一旦注册不注销,页面跳走后回调仍会执行——轻则报 Cannot read property 'xxx' of null,重则持续耗电、干扰其他页面逻辑。
实操建议:
- 用具名函数引用注册,确保能精准注销:
const handle = res => { /* 处理逻辑 */ };→uni.onAccelerometerChange(handle)→uni.offAccelerometerChange(handle) - 在
onHide或onUnload中配对调用uni.offAccelerometerChange(handle)和uni.stopAccelerometer() - 不要依赖
clearTimeout动态取消防抖定时器——摇动持续时间不可控,固定 1500ms 锁定期比反复重置更可靠
H5 和小程序端根本不能用这套逻辑
uni.onAccelerometerChange 在 H5 端完全不可用(浏览器策略限制),模拟器也无效;微信/支付宝等小程序虽有接口,但行为不一致,部分平台返回空对象或静默失败。这不是你代码的问题,是平台能力边界。
实操建议:
- 真机调试必须用 iOS 或 Android 设备,H5 环境下别白费力气测加速度逻辑
- 若需 Web 端类似功能,改用
window.addEventListener('devicemotion', ...),但需注意:iOS Safari 默认禁用该事件,必须先调DeviceMotionEvent.requestPermission()并由用户点击授权 - 跨端项目务必做平台判断:
if (process.env.UNI_PLATFORM === 'app') { /* 加速度逻辑 */ }
真正难的不是写监听,而是让判定在不同握持姿势、不同省电策略、不同系统版本下都稳定触发——滤波方式、阈值组合、生命周期钩子顺序,三者缺一不可。


















