HTML5提供的设备运动能力让网页可以直接读取手机内部的加速度计和陀螺仪数据,从而在不安装原生应用的情况下实现摇一摇、计步、体感游戏等功能。最核心的接口是DeviceMotionEvent,它在设备发生移动或旋转时向页面推送运动数据。

一、DeviceMotionEvent基础概念
DeviceMotionEvent是浏览器在设备运动时抛出的事件对象,开发者通过监听window上的devicemotion事件来获取数据。与仅描述设备方向的DeviceOrientationEvent不同,DeviceMotionEvent关注的是运动过程中的加速度和旋转速率,能反映设备是否在移动、如何移动。
该事件对象主要包含以下几个字段:acceleration表示不含重力影响的加速度,accelerationIncludingGravity表示包含重力在内的加速度,rotationRate描述设备绕三个坐标轴的旋转角速度,interval则是数据刷新间隔时间,单位为毫秒。理解这几个字段的区别,是正确使用API的前提。
1.1 坐标系说明
设备运动API采用以设备屏幕为基准的右手坐标系。x轴沿屏幕水平向右,y轴沿屏幕垂直向上,z轴垂直于屏幕向外。当手机平放在桌面上屏幕朝上时,z轴方向的accelerationIncludingGravity约为9.8,这是因为重力向下作用于设备背面,而z轴正向朝外。
如果只观察acceleration字段,静止平放的手机三轴数值都接近0,因为此时没有相对运动;但accelerationIncludingGravity的z轴会显示约9.8。很多初学者误把accelerationIncludingGravity当作纯粹的运动加速度,导致摇晃检测阈值设置错误。
二、基础用法与代码示例
在标准浏览器中,只需为window绑定devicemotion事件即可接收数据。下面是一段最基础的监听代码,将运动数据打印到控制台并展示在页面上。
window.addEventListener('devicemotion', function(event) {
var acc = event.accelerationIncludingGravity;
var rot = event.rotationRate;
console.log('x加速度:' + acc.x);
console.log('y加速度:' + acc.y);
console.log('z加速度:' + acc.z);
console.log('绕x轴旋转:' + rot.beta);
console.log('绕y轴旋转:' + rot.gamma);
console.log('绕z轴旋转:' + rot.alpha);
});
上述代码在安卓设备的大多数浏览器中可以直接运行。但在iOS 13及更高版本中,苹果要求网页必须主动调用权限申请接口,且只能在用户手势(如点击)触发下执行,否则devicemotion事件不会被派发。
因此实际项目中需要先判断DeviceMotionEvent.requestPermission是否存在,存在则先申请。下面给出兼容写法,确保不同平台都能开启监听。
function startMotion() {
if (typeof DeviceMotionEvent !== 'undefined' &&
typeof DeviceMotionEvent.requestPermission === 'function') {
DeviceMotionEvent.requestPermission()
.then(function(response) {
if (response === 'granted') {
window.addEventListener('devicemotion', handleMotion);
} else {
alert('未获得运动传感器权限');
}
})
.catch(function(err) {
console.error('权限申请失败', err);
});
} else {
window.addEventListener('devicemotion', handleMotion);
}
}
function handleMotion(event) {
var acc = event.accelerationIncludingGravity;
document.getElementById('out').innerText =
'x:' + acc.x + ' y:' + acc.y + ' z:' + acc.z;
}
document.getElementById('btn').addEventListener('click', startMotion);
2.1 权限申请注意事项
requestPermission返回的是一个Promise,只能在由用户点击或触摸产生的调用栈中触发。如果把startMotion放在页面加载时自动执行,iOS会直接拒绝。因此页面上通常会放一个“开启体感”按钮,用户点按后才绑定传感器。
另外,本地以file协议打开的页面在部分浏览器中也会被限制获取传感器数据,建议在https环境下测试。非安全上下文里,devicemotion事件可能完全不触发。
三、用设备运动API实现摇一摇
摇一摇是设备运动API最典型的应用。基本原理是监测加速度变化量,当任一轴的绝对加速度在短时间内的差值超过设定阈值,就判定为一次摇晃。
下面示例记录上一次加速度,计算当前与上次的差值,若超过阈值则触发回调。为了避免过于灵敏,可加入时间间隔判断。
var lastX = 0, lastY = 0, lastZ = 0;
var lastTime = 0;
var SHAKE_THRESHOLD = 15;
function handleMotion(e) {
var acc = e.accelerationIncludingGravity;
var now = Date.now();
if (now - lastTime < 100) return;
var diff = Math.abs(acc.x - lastX) +
Math.abs(acc.y - lastY) +
Math.abs(acc.z - lastZ);
if (diff > SHAKE_THRESHOLD) {
console.log('检测到摇晃');
}
lastX = acc.x;
lastY = acc.y;
lastZ = acc.z;
lastTime = now;
}
window.addEventListener('devicemotion', handleMotion);
代码里用accelerationIncludingGravity是因为部分设备acceleration为空,只提供含重力的数值。由于静止时重力分量恒定,相减后不影响差值判断,因此用含重力字段也能工作。
阈值设定需要结合设备采样率。一般手机每秒推送约10到60次数据,SHAKE_THRESHOLD取15左右可过滤轻微抖动。若用在游戏中,可调低阈值提升灵敏度,但容易出现误触。
四、常见问题与误差分析
开发者在接入DeviceMotionEvent时常遇到数据跳变、方向混乱等问题。一方面,加速度计本身存在噪声,平放时数值也会在9.8附近小幅波动;另一方面,不同厂商对坐标的定义可能略有偏差。
另一个容易被忽略的点是rotationRate中的alpha、beta、gamma单位均为度每秒,而非角度本身。若想获取设备绝对姿态,应改用deviceorientation事件里的方向数据,而非DeviceMotionEvent的rotationRate。
| 字段 | 含义 | 单位 |
|---|---|---|
| acceleration.x | 忽略重力的x轴加速度 | m/s² |
| accelerationIncludingGravity.y | 含重力的y轴加速度 | m/s² |
| rotationRate.alpha | 绕z轴旋转角速度 | 度/秒 |
| interval | 数据刷新间隔 | 毫秒 |
4.1 降低噪声的方法
直接使用的原始数据波动较大,可在脚本中做滑动平均,比如保留最近五次读数求均值,再用于判断。这样能显著减少误触发,代价是响应延迟略微增加。
如果项目对实时性要求高,也可以采用高通滤波分离重力加速度与动态加速度,但实现复杂度较高。普通业务用阈值配合简单的防抖已足够。
五、小结
HTML5设备运动API通过DeviceMotionEvent把手机传感器能力开放给网页,核心在于正确监听devicemotion事件、理解加速度与旋转率字段,并处理iOS权限限制。掌握这些后,摇一摇、计步、体感交互都可以在纯前端完成。
实际开发时请注意运行环境必须为https、用户手势触发权限申请、以及区分含重力与不含重力加速度。把这些细节处理好,就能稳定地把设备运动数据用在产品中。
HTML5DeviceMotion_APIdeviceorientation修改时间:2026-08-04 08:30:37