uni.startAccelerometer()必须显式调用才能触发uni.onAccelerometerChange,仅注册监听无效;需配对使用start与off,且启动须在用户交互后、页面onShow或按钮点击时执行,并注意iOS隐私策略及H5不支持限制。

uni.startAccelerometer() 必须显式调用,否则 uni.onAccelerometerChange 永远不触发
很多人写了 uni.onAccelerometerChange 却收不到数据,根本原因是没启动传感器。这个 API 不是“注册即生效”,它只负责绑定回调,真正采集数据靠 uni.startAccelerometer() —— 两者必须配对使用,且 start 要在前。
常见错误包括:
- 只写监听,漏掉
uni.startAccelerometer() - 把
uni.getAccelerometerFrequency()当成启动指令(它只是设采样率,不开启采集) - 在
App.vue的onLaunch里就调start(Android 8.0+ 和多数国产 ROM 会静默拒绝)
实操建议:
- 放在页面
onShow或用户点击按钮后触发,确保有用户交互上下文(尤其 iOS WKWebView) - 加
success和fail回调,确认是否真正启动成功:uni.startAccelerometer({ interval: 'game', success: () => console.log('sensor started') }) - Android 低端机可尝试
interval: 'game'(需 HBuilderX 3.1.22+),提升采样精度
判断“摇一摇”不能只看 res.x/res.y/res.z 绝对值
直接写 Math.abs(res.x) > 30 或 res.x > 2.5 在真机上几乎必然误判:走路、放手机、按屏幕甚至呼吸都可能让单轴瞬时超限;而侧握、躺着摇又容易漏判。本质问题在于——摇一摇不是“动得大”,而是“动得快且方向突变”,即 jerk(加速度变化率)。
实操建议:
- 每帧计算合加速度模长:
const acc = Math.sqrt(x*x + y*y + z*z) - 缓存最近 3–5 帧的
acc值(用accHistory.push(acc).slice(-5)) - 用差分近似 jerk:
const jerk = Math.abs(acc - lastAcc) / (Date.now() - lastTime),单位 m/s³ - 双阈值判定:模长峰值 > 18(≈1.8g)且 jerk > 30,同时满足才记为一次有效抖动
页面卸载或切后台时必须手动 stop + off,否则内存泄漏+耗电
uni.onAccelerometerChange 是全局监听器,一旦注册不注销,页面跳走后回调仍在执行。轻则报 Cannot read property 'xxx' of null,重则持续耗电、干扰其他页面逻辑。
实操建议:
- 用具名函数引用注册,便于精确注销:
const handle = res => { /* ... */ }; uni.onAccelerometerChange(handle); - 在
onHide或onUnload中配对调用:uni.offAccelerometerChange(handle)和uni.stopAccelerometer() - 不要依赖系统自动回收——iOS 后台 10 秒内停传感器,Android 多数厂商 30 秒强制停,但监听器还在内存里挂着
iOS 真机调试失败?大概率卡在隐私策略和首次手势触发上
iOS WKWebView 从 12.2 开始强制要求:传感器访问必须由用户手势(如点击)首次触发。页面加载完自动调 startAccelerometer 会被静默拦截,控制台无任何错误提示。
实操建议:
- manifest.json → App SDK 配置 → iOS 模块权限中勾选「运动与健身」
- Info.plist 补充
NSMotionUsageDescription字符串(如“用于摇一摇互动”) - 必须加一个「开启摇一摇」按钮,点击后才调
uni.startAccelerometer() - 第一次点击生效后,后续页面返回仍需重新触发——这不是 bug,是系统设计
最易被忽略的是:H5 端压根不支持这套 API,uni.onAccelerometerChange 在浏览器里永远不触发,连模拟器都无效。真机调试必须用 iOS 或 Android 设备,别在 Chrome 里白费时间。


















