必须用uni.onaccelerometerchange配合uni.startaccelerometer(),且需通过加速度变化率(jerk)判断抖动、权限配置、ios用户手势触发、及时注销监听等完整链路才能可靠实现摇一摇。

必须用 uni.onAccelerometerChange + uni.startAccelerometer(),且不能只比单轴绝对值——否则真机上要么不触发,要么走路就响。
为什么 uni.onAccelerometerChange 没回调?先查这三件事
监听函数注册了但完全没执行,90% 是链路断在启动环节:
-
uni.startAccelerometer()没调用——它不是可选,是强制前置步骤;uni.getAccelerometerFrequency只设频率,不启动传感器 - iOS 真机未勾选 manifest.json → App SDK 配置 → 「运动与健身」权限,会静默失败,无任何错误提示
- Android 在
onLaunch或页面刚加载时就调用uni.startAccelerometer(),可能被系统拦截(尤其 MIUI/HarmonyOS),应改在onShow或用户点击按钮后触发
怎么判断“真摇一摇”,而不是放手机或走路颠簸
单看 Math.abs(res.x) > 30 这类写法,在华为 Mate 60、iPhone 15 Pro 上实测误触率超 70%。可靠做法是算加速度变化率(jerk):
- 缓存最近 4 帧的
x/y/z,用acc = Math.sqrt(x*x + y*y + z*z)算合加速度模长 - 每帧计算与前一帧的差值平方和:
dx*dx + dy*dy + dz*dz(避免开方,省性能) - 仅当该值连续 2 帧 > 20 才记为一次有效抖动;累计 3 次抖动再触发,防毛刺
- 别用固定阈值,不同机型建议起始值设为 15–25,真机调试微调
播放反馈音前必须确认音频上下文已就绪
uni.createInnerAudioContext() 创建后需显式设置 src 并等待 onCanPlay 回调,否则 iOS 上常静音:
- 提前创建并复用音频实例,不要每次摇都新建
- 播放前加
audioContext.autoplay = true和audioContext.loop = false - 安卓部分机型需振动权限支持,manifest.json 中补上:
"scope.vibrate": {"desc": "用于摇一摇反馈"} - 触发后立即调
audioContext.play(),别包在setTimeout里——延迟超 200ms 用户感知明显卡顿
页面跳走后还在响?这是没关监听导致的内存泄漏
uni.onAccelerometerChange 是全局注册,不手动注销会在后台持续执行,轻则报错 Cannot read property 'play' of null,重则耗电发热:
- 必须在
onHide或onUnload中配对调用:uni.offAccelerometerChange(handle) - 回调函数必须是同一引用,推荐写法:
const handle = res => { /* ... */ }; uni.onAccelerometerChange(handle); uni.offAccelerometerChange(handle); - 音频播放完不用主动
stop(),但要清空src防止下次播放失败
最易被忽略的是:iOS WKWebView 要求首次传感器调用必须由用户手势触发(比如点按钮),页面自动启动永远无效;且一旦页面跳转,回来得重新点一次——这个限制不是 bug,是系统策略,绕不开。











