基于加速度和重力感应判断设备横屏

前言

项目里需要根据设备拍摄横竖方向,切换屏幕上显示的内容,查阅小程序文档以及相关资料,找到两种解决方法并记录下来。

加速计

小程序的设备API中提供了加速度计的监听方法,使用方法如下:

1
2
3
4
5
wx.onAccelerometerChange(function(res) {
console.log(res.x)
console.log(res.y)
console.log(res.z)
})

以手机竖直面向用户为例,加速计的三轴坐标系统的X、Y、Z轴定义如下:

  • 沿着手机屏幕顶部向上是Y轴正方向,向下是Y轴负方向
  • 当手机顶部朝上时,沿着手机屏幕向右是X轴正方向,向左是X轴负方向
  • 正对手机时,垂直屏幕向外是Z轴正方向,垂直屏幕向里是Z轴负方向

计算姿态角

相关文献参考:https://www.nxp.com.cn/docs/en/application-note/AN3461.pdf

1
2
Pitch = atan2(Y, Z) * 180/M_PI;Roll = atan2(-X, sqrt(Y*Y + Z*Z)) * 180/M_PI;
Roll = atan2(-X, sqrt(Y*Y + Z*Z)) * 180/M_PI;

以下是具体代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
let lastTime = Date.now()
wx.onDeviceMotionChange((res) => {
const now = Date.now()
if (now - lastTime < 500) {
return
}
lastTime = now
const N = 180 / Math.PI
const Roll = Math.atan2(-res.x, Math.sqrt(res.y * res.y + res.z * res.z)) * N
const Pitch = Math.atan2(res.y, res.z) * N
let nowState
// 横屏状态
if (Roll > 50) {
if ((Pitch > -180 && Pitch < -60) || (Pitch > 130)) {
nowState = 1
} else {
nowState = lastState
}
} else if ((Roll > 0 && Roll < 30) || (Roll < 0 && Roll > -30)) {
const absPitch = Math.abs(Pitch)

// 如果手机平躺,保持原状态不变,40容错率
if ((absPitch > 140 || absPitch < 40)) {
nowState = lastState
} else if (Pitch < 0) { // 收集竖向正立的情况
nowState = 0
} else {
nowState = lastState
}
} else {
nowState = lastState
}
// 状态变化时,触发
if (nowState !== lastState) {
lastState = nowState
if (nowState === 1) {
console.log('change:横屏')
} else {
console.log('change:竖屏')
}
}
})

重力感应

使用 wx.onDeviceMotionChange 监听设备alpha,beta以及gamma值

属性 说明
alpha 当 手机坐标 X/Y 和 地球 X/Y 重合时,绕着 Z 轴转动的夹角为 alpha,范围值为 [0, 2 * PI)。逆时针转动为正。
beta 当手机坐标 Y/Z 和地球 Y/Z 重合时,绕着 X 轴转动的夹角为 beta。范围值为 [-1 * PI, PI) 。顶部朝着地球表面转动为正。也有可能朝着用户为正。
gamma 当手机 X/Z 和地球 X/Z 重合时,绕着 Y 轴转动的夹角为 gamma。范围值为 [-1*PI/2, PI/2)。右边朝着地球表面转动为正。

以下是具体代码

1
2
3
4
5
6
7
8
9
10
11
12
wx.onDeviceMotionChange((res) => {
const alpha = parseFloat(res.alpha)
if (alpha > 45 && alpha < 136) {
// 左侧
} else if (alpha > 225 && alpha < 316) {
// 右侧
} else if (alpha > 135 && alpha < 226) {
// 反面
} else {
// 正面
}
})