一、devicemotion事件
在实现移动端摇一摇功能前,需要大家先了解移动端的devicemotion
事件,以及防抖节流的知识,这样有助于大家理解
监控横竖屏切换检测
监听orientationchange
时间,通过window.orientation
属性判断当前屏幕属于横屏(90,-90)还是竖屏(0,180)
function setOrientation() {
let mask = document.querySelector("#mask");
switch (window.orientation) {
case 90:
case -90:
mask.style.display = "flex";
break;
default:
mask.style.display = "none";
break;
}
}
setOrientation();
window.addEventListener("orientationchange",setOrientation)
手机加速度检测
主要监听devicemotion
事件,acceleration
就是获取手机的加速度
window.addEventListener("devicemotion",(e)=>{
const motion = e.acceleration; // 手机加速度
const {x,y,z} = motion;
box.innerHTML = `
x:${x.toFixed(0)}<br/>
y:${y.toFixed(0)}<br/>
z:${z.toFixed(0)}
`;
});
手机重力加速度
手机重力加速度很加速度很相似,就是属性名不同,其为accelerationIncludingGravity
window.addEventListener("devicemotion",(e)=>{
const motion = e.accelerationIncludingGravity; // 手机加速度 + 重力
const {x,y,z} = motion;
box.innerHTML = `
x:${x.toFixed(0)}<br/>
y:${y.toFixed(0)}<br/>
z:${z.toFixed(0)}
`;
});
手机收到的重力
前面我们讲到accelerationIncludingGravity
为手机加速度+重力,acceleration
为加速度,则两者相减就能得到重力
window.addEventListener("devicemotion",(e)=>{
const motion = e.accelerationIncludingGravity; // 手机加速度 + 重力
const motion2 = e.acceleration; // 加速度
let {x,y,z} = motion;
let {x:x2,y:y2,z:z2} = motion2;
x -= x2;
y -= y2;
z -= z2;
box.innerHTML = `
x:${x.toFixed(0)}<br/>
y:${y.toFixed(0)}<br/>
z:${z.toFixed(0)}
`;
});
体感操作
利用translateX
、translateY
let translateX = 0;
let translateY = 0;
window.addEventListener("devicemotion",(e)=>{
const motion = e.accelerationIncludingGravity; // 手机加速度 + 重力
const motion2 = e.acceleration; // 加速度
let {x,y} = motion;
let {x:x2,y:y2} = motion2;
x -= x2;
y -= y2;
translateX += x;
translateY -= y;
box.style.transform = `translate(${translateX}px,${translateY}px)`;
});
踩坑
加速度在检测过程中,会存在一些坑:
-
安卓下 和 IOS 下,加速度方向取值相反
-
IOS 中,如果要使用加速度 API,当前应用则必须使用 https 协议
-
IOS 12.2 中,用户可以在手机设置中关闭掉,动作与方向的访问权限
-
IOS 13 及之后,当应用中想要使用动作与方向的访问权限时,需要请求用户授权
-
IOS 13.3 及之后,申请授权,必须用户手动触发
针对上述的坑,我们来一步步解决
由于在安卓下 和 IOS 下,加速度方向取值相反,我们可以编写一个方法判断是否为安卓
function isAndroid() {
const u = window.navigator.userAgent;
return u.indexOf("Android")>-1||u.indexOf("Adr")>-1;
}
IOS 12.2 中,如果用户在在手机设置中设置关闭了动作与方向的访问权限,那么我们就监听不了devicemotion
事件
我们可以在页面一开始渲染的时候就触发一个延时器提示用户设置权限,再通过监听事件,清楚这个延时器
let timer = setTimeout(() => {
alert("请开启动作与方向的访问权限,否则将无法使用本应用");
}, 200);
window.addEventListener("devicemotion",()=>{
clearTimeout(timer);
},{once:true});
IOS 13.3 及之后,申请授权,必须用户手动触发。这里的意思大概就是,我们不能把加速度的监听事件,直接在挂载的时候渲染。需要在DOM
事件触发后执行
btn.addEventListener("touchend",()=>{
setMotion((e) => {
const motion = e.accelerationIncludingGravity; // 手机加速度 + 重力
const motion2 = e.acceleration;
let { x, y } = motion;
let { x: x2, y: y2 } = motion2;
x -= x2;
y -= y2;
translateX += x;
translateY -= y;
box.style.transform = `translate(${translateX}px,${translateY}px)`;
});
});
二、防抖与节流
防抖
简单来讲,就是希望函数只执行一次,哪怕我进行了多次调用
下面给出一个简单版本的防抖函数封装:
其中,fn
为需要防抖的函数,delay
为防抖时长,start
为是否在开始时执行函数,return
返回经过近防抖处理的函数
防抖主要需要处理的两点地方:
- 处理
this
指向问题 - 处理参数问题
function debounce(fn,deley = 200,start = false) {
let timer = 0;
let isStart = true;
return function(...arg) { // 经过防抖处理的函数
const _this = this;
clearTimeout(timer);
if(isStart){
start && fn.apply(_this,arg);
isStart = false;
}
timer = setTimeout(() => {
(!start)&&fn.apply(_this,arg);
isStart = true;
}, deley);
}
}
节流
简单来讲,让函数保持在一个可接受的固定频率执行
节流跟防抖十分的相似,无非就是判断的时候处理方式不同
function throttle(fn,deley = 200,start = true) {
let timer = 0;
return function(...arg) { // 经过防抖处理的函数
const _this = this;
if(timer){
return;
}
start&&fn.apply(_this,arg);
timer = setTimeout(() => {
(!start)&&fn.apply(_this,arg);
timer = 0;
}, deley);
}
}
三、实现过程
实现摇一摇的本质就是:当前次的加速度和上一次的加速之间有了一个比较大的差值
下面定义两个属性来判断
const maxRange = 50; // 当加速度的差值大于该值时认定用户进行了摇一摇
const minRange = 5; // 当加速度的差值小于于该值时认定用户停止了摇一摇
获取手机加速度
const { x, y, z } = e.acceleration;
const range = Math.abs(x - lastX) + Math.abs(y - lastY) + Math.abs(z - lastZ);
后面的功能就没什么难点,如果看到不懂的地方,涉及的知识点文章开头就已经涉及,这里是做了兼容 IOS(多个版本) 与 Anroid 的处理,再加上防抖功能,让摇一摇功能更加高效
下面给出完整代码
<body>
<button id="btn">开启摇一摇</button>
<button id="stopBtn">关闭摇一摇</button>
<div id="info"></div>
<script src="motion.js"></script>
<script>
let btn = document.querySelector("#btn");
let stopBtn = document.querySelector("#stopBtn");
/*
setShake 摇一摇
ops : {
start:fn // 开始摇一摇时要做的事情
shake:fn // 摇一摇中要做得事情
end: fn// 摇一摇结束后要做的事情
}
*/
function setShake(ops) {
const { start = () => { }, shake = () => { }, end = () => { } } = ops;
let lastX = 0,
lastY = 0,
lastZ = 0;
const maxRange = 50;
const minRange = 5;
let isShake = false;
const unMotion = setMotion(throttle((e) => {
const { x, y, z } = e.acceleration;
const range = Math.abs(x - lastX) + Math.abs(y - lastY) + Math.abs(z - lastZ);
if (range > maxRange && (!isShake)) {
start(e);
isShake = true;
} else if (range > maxRange && isShake) {
shake(e);
} else if (range < minRange && isShake) {
end(e);
isShake = false;
}
lastX = x;
lastY = y;
lastZ = z;
}));
return unMotion; //取消摇一摇监听
}
let unShake;
btn.addEventListener("touchend", () => {
unShake = setShake({
start:()=>{
info.innerHTML += "开始摇一摇<br/>";
},
shake:()=>{
info.innerHTML += "摇一摇中<br/>";
},
end: ()=>{
info.innerHTML += "摇一摇结束<br/>";
}
})
});
stopBtn.addEventListener("touchend",()=>{
if(unShake){
unShake();
}
})
</script>
</body>
motion.js
// 在 IOS 12 中,判断用户是否关闭了动作与方向的访问权限
{
let timer = setTimeout(() => {
alert("请开启动作与方向的访问权限,否则将无法使用本应用");
}, 200);
window.addEventListener("devicemotion", () => {
clearTimeout(timer);
}, { once: true });
}
// 判断当前是否是安卓系统
function isAndroid() {
const u = window.navigator.userAgent;
return u.indexOf("Android") > -1 || u.indexOf("Adr") > -1;
}
/*
setMotion 设置监听加速变化要处理的事情
cb 加速度变化后要做的处理函数
return 取消事件注册
*/
function setMotion(cb) {
let fn = (e) => {
if (isAndroid()) { // 处理安卓取反问题
e.acceleration.x = -e.acceleration.x;
e.acceleration.y = -e.acceleration.y;
e.acceleration.z = -e.acceleration.z;
e.accelerationIncludingGravity.x = -e.accelerationIncludingGravity.x;
e.accelerationIncludingGravity.y = -e.accelerationIncludingGravity.y;
e.accelerationIncludingGravity.z = -e.accelerationIncludingGravity.z;
}
cb(e);
};
// 区分 IOS 13 及之前
if (typeof DeviceMotionEvent.requestPermission === "function") { // IOS 13 及之后
DeviceMotionEvent.requestPermission()
.then(permissionState => {
if (permissionState === 'granted') {
// 权限允许
window.addEventListener("devicemotion", fn);
}
}).catch(() => {
alert("请开启授权否则无法使用本应用");
})
} else { //安卓及IOS 13之前
window.addEventListener("devicemotion", fn)
}
return ()=>{
window.removeEventListener("devicemotion",fn);
}
}
四、结束语
如果觉得这篇文章对你有帮助,可以伸出你的小手,为这篇文章点个赞
我是前端路上一位新晋的萌新,怀着学习的态度,怀着认识各位同伴的心态,把自己的知识分享出来,除了让自己对知识认知更加巩固,也希望大家能够通过我写的文章学到一点微薄的知识,如果知识内容有误,可以在评论区或者下面公众号告诉我,我会立刻更改
最后,我也创建了一个 【前端收割机】的公众号,希望大家可以关注一波,里面的文章都是掉头发之作
常见问题FAQ
- 免费下载或者VIP会员专享资源能否直接商用?
- 本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
- 提示下载完但解压或打开不了?
- 找不到素材资源介绍文章里的示例图片?
- 模板不会安装或需要功能定制以及二次开发?
发表评论
还没有评论,快来抢沙发吧!