关于陀螺仪的一些问题
# 关于陀螺仪的使用
# 前景
在做个人首页的时候,PC端做了一个 跟随鼠标旋转的效果,但是在移动端时,又没有鼠标,那么旋转要根据什么判断呢?
于是想到了,可以判断重力感应 (陀螺仪) 来实现旋转
也是因为种种原因,最后在 移动端,是以小球来做的跟随陀螺仪的移动
# 技术
这里主要用到的 api 为 DeviceOrientationEvent (opens new window)
DeviceOrientationEvent
会返回当前设备在浏览页面是的物理旋转信息
我们这里会用到的信息主要有 3 个
alpha
: 一个表示设备绕 z 轴旋转的角度(范围在 0-360 之间)的数字
beta
: 一个表示设备绕 x 轴旋转(范围在-180 到 180 之间)的数字,从前到后的方向为正方向。
gamma
: 一个表示设备绕 y 轴旋转(范围在-90 到 90 之间)的数字,从左向右为正方向。
而 做小球跟随陀螺仪移动的效果,我们只需要用到 beta
和 gamma
# 实现
首先我们需要监听 DeviceOrientationEvent
事件,来获取到 陀螺仪的信息
window.addEventListener("deviceorientation", handleOrientation, true);
function handleOrientation(event) {
var x = event.beta; // range [-180,180]
var y = event.gamma; // range [-90,90]
}
2
3
4
5
6
监听获取到 陀螺仪的 信息之后,这里可以拿到 beta
和 gamma
的值
可以看到 beta 在 -180 和 180 之间, gamma 在 -90 到 90 之间
我们可以通过 chrome 的模拟器来模拟一下 这些角度下,手机都是什么状态
通过 chrome 的 sensors 可以来模拟手机陀螺仪,开启方法如下
打开之后可以看到会出现手机的模拟状态
可以看到 在 beta
90°
时,手机是竖直向下的
我们可以试着调整角度来模拟不同的状态
我们调整 gamma
的角度来看看手机的情况
可以看到,在 70° 时,手机是向右旋转的,同理可以推算 90°时,手机正面完全转到右侧,反之如果度数为负值,则是向左旋转
接着调整 beta
的角度来观察手机的状态
旋转 20°
旋转 150°
旋转 -20°
可以看到 0-180 是属于手机正面的旋转角度
-180 - 0 则是手机背面的
对此,我们这里采用的事手机正面的陀螺仪 ,背面的则不做处理,至此,我们已经拿到了 手机的角度,以及各个角度所表达的意思,那么接下来就是 小球以及小球动画的问题
# 小球及动画
这里我创建了一个 小球,默认就当做是在屏幕的正中央,宽度为 20vmin
首先拿到设备的宽和高
viewportH = window.innerHeight || document.documentElement.clientHeight
viewportW = window.innerWidth || document.documentElement.clientWidth
2
接着确定小球的宽度,我这里是使用 border
实现的,所以取 0.1 即可
_ball = viewportH > viewportW ? viewportW * 0.1 : viewportH * 0.1;
此时就确定了,小球的直径,以及获取到了设备的宽高,接着我们要确定一些安全距离
maxX = viewportW - ballSize
maxY = viewportH - ballSize
midX = maxX / 2
midY = maxY / 2
2
3
4
我们是通过 left 和 top 来修改小球的位置的,那么 left
最大的值,则就是 设备宽度 减去 小球的直径,top 同理
这里同时也取到了 中位数,也就是小球默认所处的位置 left
和 top
的值
这些是我们获取到的初始状态,那么此时就可以根据这些值 还有 陀螺仪的角度来对应修改小球的位置
window.addEventListener("deviceorientation", handleOrientation, true);
function handleOrientation(event) {
var x = event.beta;
var y = event.gamma;
if (x > 90) { x = 90}
if (x < -90) { x = -90}
ball.css({
top: ( midY - maxY * x / 180 ) + "px",
left: ( midX - maxX * y / 180 ) + "px"
})
}
2
3
4
5
6
7
8
9
10
11
首先 对于 x 也就是 beta 的值做了一个修正,我们取 -90 - 90 之间的度数,也就是手机 正面朝上 然后 上下倾斜的度数
对此就做了一个修正
if (x > 90) { x = 90}
if (x < -90) { x = -90}
2
接着 可以看到 x
和 y
都是 180 度的跨度,那么 就除以 180
,拿到当前度数对应的距离,得到的值有正有负
通过之前得出的 中位数 midY
和 midX
,来算出 上下左右的偏移,同步修改 ball
所对应的 css
即可
至此,正常的陀螺仪小球动画就完成了,当然也有很多其他的效果,可以自己尝试一下
# iOS 设备的相关问题
在 iOS
中,按照上述步骤完成后,是不能实现小球动画的,那是因为 iOS
的陀螺仪做了一定的限制
- 项目的地址必须是
https
协议 - 在 iOS 13+ ,需要申请用户权限
window.DeviceOrientationEvent.requestPermission()
, 返回值是个promise
,promise
中会返回 用户的 允许(“granted
”)/ 拒绝(”denied
“) 状态; requestPermission
方法的 首次 调用 需要 由用户交互触发(目前发现click
,touchend
事件可以)- 先说下 首次 的含义。指的是用户 完全退出
app
(而不是 app 切换到后台运行) 后再次打开为首次。用户在首次进入后 如果页面需要陀螺仪权限,需要用户交互才能触发requestPermission
方法。之后系统会记录 用户对本网址的授权信息,不退出app
的情况下就不需要重复申请本权限了,本记录信息会一直保留 直到完全退出app
。 - 非首次调用的话 则不需要用户交互 触发,我们在页面初始化时调用来拿到 用户的 授权状态 进而做一些处理。
- 如果首次调用 不是由 用户触发,比如 页面初始化时 调用,则此方法的
promise
会返回reject
状态
- 先说下 首次 的含义。指的是用户 完全退出
基于以上问题,对于 ios 端做一定的兼容处理,将事件绑定在 body
上
isIOS = !!navigator.userAgent.match(/(iPhone|iPad|iPod|iOS)/i);
if (isIOS && typeof window.DeviceMotionEvent.requestPermission === 'function') {
$(document).one('click', 'body', function () {
window.DeviceMotionEvent.requestPermission()
.then(permissionState => {
if (permissionState === 'granted') {
window.addEventListener('deviceorientation', handleOrientation);
}
})
.catch(console.error);
})
} else {
window.addEventListener("deviceorientation", handleOrientation, true);
}
2
3
4
5
6
7
8
9
10
11
12
13
14
对于 iOS 没有点击授权之前,可以考虑做一个小球自动反弹的动画,那个是后话了,或者加一点其他的状态,提醒用户点击授权等等 ~