Device Orientation 事件
一、设备传感器 API 概述
1.1 什么是设备方向事件
设备方向事件允许 Web 应用检测设备的物理方向和运动。这些数据来自设备内置的传感器:陀螺仪、指南针和加速度计。移动设备通常具备这些传感器,但桌面设备可能不具备。
// 监听设备方向变化
window.addEventListener('deviceorientation', (event) => {
console.log('Alpha:', event.alpha); // Z轴旋转 0-360
console.log('Beta:', event.beta); // X轴旋转 -180 到 180
console.log('Gamma:', event.gamma); // Y轴旋转 -90 到 90
});
1.2 相关事件
| 事件 | 描述 | 数据来源 |
|---|---|---|
deviceorientation |
设备朝向变化 | 陀螺仪 + 指南针 |
deviceorientationabsolute |
绝对方向变化 | 融合传感器 |
devicemotion |
设备运动/加速度 | 加速度计 |
二、deviceorientation 事件
2.1 旋转角度
| 属性 | 描述 | 范围 |
|---|---|---|
alpha |
Z轴旋转(指南针方向) | 0° - 360° |
beta |
X轴旋转(前后倾斜) | -180° - 180° |
gamma |
Y轴旋转(左右倾斜) | -90° - 90° |
设备方向示意图:
Beta (X轴旋转)
↓
+---+
| | ← 手机直立
| |
+---+ alpha=0
↑
手机背面
Gamma (Y轴旋转)
+---+
| | ← 手机左右倾斜
+---+
↙
左边翘起为负,右边翘起为正
2.2 实际应用
// 3D 物体旋转控制
window.addEventListener('deviceorientation', (e) => {
const { alpha, beta, gamma } = e;
// 将角度转换为弧度
const radAlpha = alpha * Math.PI / 180;
const radBeta = beta * Math.PI / 180;
const radGamma = gamma * Math.PI / 180;
// 应用到 3D 物体
cube.rotation.z = radAlpha;
cube.rotation.x = radBeta;
cube.rotation.y = radGamma;
});
2.3 WebXR 中的应用
// 在 WebXR 中使用设备方向
if ('DeviceOrientationEvent' in window) {
window.addEventListener('deviceorientation', (e) => {
// 校准设备方向
const quaternion = e quaternion;
// 应用到 XR 控制器
controller.quaternion.set(
quaternion.x,
quaternion.y,
quaternion.z,
quaternion.w
);
});
}
三、devicemotion 事件
3.1 加速度数据
window.addEventListener('devicemotion', (event) => {
const { x, y, z } = event.accelerationIncludingGravity;
// acceleration:不含重力的加速度
// accelerationIncludingGravity:含重力影响的加速度
console.log(`Acceleration: ${x}, ${y}, ${z}`);
});
3.2 旋转速率
window.addEventListener('devicemotion', (event) => {
const { alpha, beta, gamma } = event.rotationRate;
console.log(`Rotation rate: α=${alpha}, β=${beta}, γ=${gamma}`);
});
3.3 摇动手势检测
class ShakeDetector {
constructor(threshold = 15) {
this.threshold = threshold;
this.lastX = this.lastY = this.lastZ = 0;
this.lastTime = Date.now();
window.addEventListener('devicemotion', (e) => this.handleMotion(e));
}
handleMotion(event) {
const { x, y, z } = event.accelerationIncludingGravity;
const currentTime = Date.now();
const timeDiff = currentTime - this.lastTime;
if (timeDiff > 100) {
const deltaX = Math.abs(x - this.lastX);
const deltaY = Math.abs(y - this.lastY);
const deltaZ = Math.abs(z - this.lastZ);
const acceleration = (deltaX + deltaY + deltaZ) / timeDiff * 10000;
if (acceleration > this.threshold) {
this.onShake();
}
this.lastX = x;
this.lastY = y;
this.lastZ = z;
this.lastTime = currentTime;
}
}
onShake() {
console.log('Device shaken!');
}
}
四、权限请求
4.1 iOS 13+ 需要权限
// iOS 13+ 需要用户授权
async function requestOrientationPermission() {
if (typeof DeviceOrientationEvent.requestPermission === 'function') {
try {
const permission = await DeviceOrientationEvent.requestPermission();
if (permission === 'granted') {
enableOrientation监听();
}
} catch (error) {
console.error('Permission denied:', error);
}
} else {
// 非 iOS 设备不需要请求权限
enableOrientation监听();
}
}
function enableOrientation监听() {
window.addEventListener('deviceorientation', handleOrientation);
}
// 在用户交互中调用
document.querySelector('#start-btn').addEventListener('click', () => {
requestOrientationPermission();
});
4.2 完整示例
class DeviceOrientationController {
constructor() {
this.beta = 0;
this.gamma = 0;
this.alpha = 0;
}
async init() {
// iOS 13+ 需要权限
if (typeof DeviceOrientationEvent.requestPermission === 'function') {
const permission = await DeviceOrientationEvent.requestPermission();
if (permission !== 'granted') {
console.warn('Device orientation permission denied');
return false;
}
}
window.addEventListener('deviceorientation', (e) => this.handleOrientation(e));
return true;
}
handleOrientation(event) {
this.beta = event.beta || 0; // X轴: -180 到 180
this.gamma = event.gamma || 0; // Y轴: -90 到 90
this.alpha = event.alpha || 0; // Z轴: 0 到 360
}
getQuaternion() {
// 将欧拉角转换为四元数(用于 WebXR)
const r = (this.alpha || 0) * Math.PI / 180;
const p = (this.beta || 0) * Math.PI / 180;
const y = (this.gamma || 0) * Math.PI / 180;
const cy = Math.cos(y * 0.5);
const sy = Math.sin(y * 0.5);
const cp = Math.cos(p * 0.5);
const sp = Math.sin(p * 0.5);
const cr = Math.cos(r * 0.5);
const sr = Math.sin(r * 0.5);
return {
w: cr * cp * cy + sr * sp * sy,
x: sr * cp * cy - cr * sp * sy,
y: cr * sp * cy + sr * cp * sy,
z: cr * cp * sy - sr * sp * cy
};
}
}
五、安全与隐私
5.1 HTTPS 要求
// 设备方向事件需要安全上下文
if (window.isSecureContext) {
console.log('Device orientation available');
} else {
console.warn('Device orientation requires HTTPS');
}
5.2 权限层级
| 设备 | 权限要求 |
|---|---|
| iOS 13+ | 显式用户授权 |
| Android | 通常自动授予(少数情况需要) |
| 桌面浏览器 | 模拟器/部分浏览器支持 |
六、面试高频问题
Q: deviceorientation 和 devicemotion 有什么区别?
回答要点:deviceorientation 报告设备朝向变化(alpha/beta/gamma 旋转角度),数据来自陀螺仪和指南针;devicemotion 报告设备运动/加速度(x/y/z加速度和旋转速率),数据来自加速度计。前者用于方向控制,后者用于运动检测。
Q: 为什么 iOS 13+ 需要请求设备方向权限?
回答要点:Apple 为保护用户隐私,将 DeviceOrientationEvent 列入了需要明确授权的 API。用户必须通过点击等交互行为触发权限请求,而不是页面加载时自动请求。
Q: 如何在 Web 应用中使用设备方向控制 3D 物体?
回答要点:监听 deviceorientation 事件获取 alpha/beta/gamma 角度,将这些角度转换为 3D 框架(如 Three.js)使用的旋转格式(通常是四元数或欧拉角),应用到物体或相机的 rotation 属性。