uni-app摇一摇需规避单轴阈值误判,改用加速度变化率(jerk)判定,并严格处理权限、生命周期、iOS手势触发、防抖清理及省电模式兼容性。

uni-app 实现摇一摇不能只靠 uni.onAccelerometerChange 监听原始加速度值就完事——直接比对 Math.abs(res.x) > 30 这类写法在真机上几乎必然误判或漏判,尤其在华为/小米省电模式、iOS WKWebView 首次交互未触发、H5 环境下根本无数据返回。
uni.startAccelerometer() 启动后没反应?先检查三件事
这不是代码 bug,而是权限和生命周期链路断了:
-
manifest.json中必须勾选「加速度传感器」权限(App端),iOS 还得在Info.plist补充NSMotionUsageDescription字符串; - 不能在
App.vue的onLaunch里全局启动——Android 8.0+ 和多数国产 ROM 会静默拒绝后台传感器访问,应改在页面onShow调用uni.startAccelerometer,onHide或onUnload立即调用uni.stopAccelerometer; - H5 端压根不支持
uni.getAccelerometerFrequency和uni.onAccelerometerChange,别白费力气调试;要用window.addEventListener('devicemotion', ...),但必须确保 WebView 已启用传感器透传(Cordova 需装cordova-plugin-device-motion,Capacitor 需先Permissions.requestPermission('accelerometer'))。
为什么 Math.abs(x) > 1.5 这种单轴阈值总是不准
人躺着摇、侧握手机、边走边摇时,单轴加速度可能始终低于阈值;而走路颠簸、放手机到桌面、甚至按屏幕的微震,又容易让某轴瞬时超限。真正有效的判定依据是「加速度变化率(jerk)」,即单位时间内的加速度差值:
- 用欧氏模长算合加速度:
const acc = Math.sqrt(x*x + y*y + z*z); - 记录上一帧时间
lastTime和上一帧合加速度lastAcc; - 计算变化率:
const jerk = Math.abs(acc - lastAcc) / (Date.now() - lastTime); - 连续 2–3 帧
jerk > 20(单位 m/s²²)才记为一次有效抖动,避免单次毛刺干扰。
iOS 真机调试失败?大概率卡在隐私策略上
iOS WKWebView 从 12.2 开始强制要求:传感器事件必须由用户手势(如点击按钮)首次触发后才能激活,且仅生效一次。这意味着:
- 不能在页面加载完自动启动监听,必须加一个「开启摇一摇」按钮,点击后调用
uni.startAccelerometer; - 第一次点击后,
uni.onAccelerometerChange才会收到数据;后续页面跳转再回来,需重新触发; - 若用户从未点过该按钮,
uni.onAccelerometerChange永远不会执行,控制台也无报错——这是系统级静默拦截,不是 uni-app 的问题。
防抖和清理不彻底,会导致连续触发或内存泄漏
一次摇一摇判定成功后,必须立刻做两件事:
- 清空历史缓存变量(如
lastAcc、lastTime、抖动计数器),否则下一秒轻微晃动就可能凑够条件; - 调用
uni.offAccelerometerChange解绑监听,并在onUnload或onHide生命周期中再次确认解绑——否则页面隐藏后仍在后台收数据,Android 会因持续占用传感器被系统降频甚至杀进程; - 加 800ms 锁定窗口:触发后停用监听 800ms,防止手还没停稳就重复响应。
最常被忽略的点是「省电模式兼容性」:华为/小米部分机型在省电模式下会把加速度计频率砍到 5Hz 甚至暂停,此时 jerk 计算完全失真。建议在首次启动时检测是否获取到连续数据,若 2 秒内无回调,弹出轻提示:“请关闭省电模式以保障摇一摇体验”。











