I. Devicemotion Event
Before implementing the shake function on the mobile terminal, you need to understand the devicemotion event on the mobile terminal and the knowledge of anti-shake throttling, so that you can understand
Monitor horizontal and vertical screen switching detection
Monitor the time of orientationchange and judge whether the current screen belongs to landscape (90, -90) or portrait (0,180) through the window.orientation attribute.
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)
Copy the code
Mobile phone acceleration detection
Basically listening for devicemotion, and the acceleration is the acceleration of the phone
window.addEventListener("devicemotion".(e) = >{
const motion = e.acceleration; // Cell phone acceleration
const {x,y,z} = motion;
box.innerHTML = `
x:${x.toFixed(0)}<br/>
y:${y.toFixed(0)}<br/>
z:${z.toFixed(0)}
`;
});
Copy the code
Gravity acceleration of mobile phone
Mobile acceleration of gravity is acceleration is similar to the property name is different, it is accelerationIncludingGravity
window.addEventListener("devicemotion".(e) = >{
const motion = e.accelerationIncludingGravity; // Mobile acceleration + gravity
const {x,y,z} = motion;
box.innerHTML = `
x:${x.toFixed(0)}<br/>
y:${y.toFixed(0)}<br/>
z:${z.toFixed(0)}
`;
});
Copy the code
The gravity received by the phone
In front of us talked about accelerationIncludingGravity for mobile + gravity acceleration, acceleration is the acceleration, the subtraction can get both gravity
window.addEventListener("devicemotion".(e) = >{
const motion = e.accelerationIncludingGravity; // Mobile acceleration + gravity
const motion2 = e.acceleration; / / 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)}
`;
});
Copy the code
Body feeling operation
Use translateX, translateY
let translateX = 0;
let translateY = 0;
window.addEventListener("devicemotion".(e) = >{
const motion = e.accelerationIncludingGravity; // Mobile acceleration + gravity
const motion2 = e.acceleration; / / 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)`;
});
Copy the code
Hit the pit
In the detection process of acceleration, there will be some pits:
-
On Android and IOS, the acceleration is in opposite directions
-
In IOS, to use the acceleration API, the current application must use HTTPS
-
In IOS 12.2, users can turn off action and direction access in their phone Settings
-
In IOS 13 and beyond, when you want to use action and direction access in an app, you need to request permission from the user
-
IOS 13.3 and later, users must manually apply for authorization
For the pit mentioned above, let’s solve it step by step
And since the acceleration is in opposite directions on Android and IOS, we can write a way to tell if it’s Android
function isAndroid() {
const u = window.navigator.userAgent;
return u.indexOf("Android") > -1||u.indexOf("Adr") > -1;
}
Copy the code
In IOS 12.2, if the user disables action and direction access in the phone’s Settings, we cannot listen for devicemotion events
We can trigger a delay timer at the beginning of the page rendering to prompt the user to set permissions, and then listen for events to know this delay timer
let timer = setTimeout(() = > {
alert("Please enable action and direction access, otherwise you will not be able to use this application");
}, 200);
window.addEventListener("devicemotion".() = >{
clearTimeout(timer); }, {once:true});
Copy the code
IOS 13.3 and later, users must manually apply for authorization. The idea here is that we can’t render the accelerometer directly at mount time. It needs to be executed after a DOM event is triggered
btn.addEventListener("touchend".() = >{
setMotion((e) = > {
const motion = e.accelerationIncludingGravity; // Mobile acceleration + gravity
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)`;
});
});
Copy the code
Second, anti-shake and throttling
Image stabilization
Basically, I want the function to only execute once, even if I make multiple calls
Here is a simple version of the anti – shake function package:
Fn is the function that needs to be buffeted, delay is the duration of buffeting, start is whether to execute the function at the beginning, and return returns the function that has been buffeted
There are two main points to deal with:
- To deal with
this
Pointing to problems - Dealing with parameter issues
function debounce(fn,deley = 200,start = false) {
let timer = 0;
let isStart = true;
return function(. arg) { // The function is buffered
const _this = this;
clearTimeout(timer);
if(isStart){
start && fn.apply(_this,arg);
isStart = false;
}
timer = setTimeout(() = > {
(!start)&&fn.apply(_this,arg);
isStart = true; }, deley); }}Copy the code
The throttle
In short, let the function execute at an acceptable fixed frequency
Throttling and anti – shake very similar, but is the judgment of the time processing way is different
function throttle(fn,deley = 200,start = true) {
let timer = 0;
return function(. arg) { // The function is buffered
const _this = this;
if(timer){
return;
}
start&&fn.apply(_this,arg);
timer = setTimeout(() = > {
(!start)&&fn.apply(_this,arg);
timer = 0; }, deley); }}Copy the code
Third, the implementation process
The essence of achieving a shake is that there is a large difference between the current acceleration and the previous acceleration
Let’s define two properties to determine
const maxRange = 50; // When the difference of acceleration is greater than this value, the user is considered to have shaken
const minRange = 5; // When the difference of acceleration is less than this value, the user has stopped shaking
Copy the code
Get phone acceleration
const { x, y, z } = e.acceleration;
const range = Math.abs(x - lastX) + Math.abs(y - lastY) + Math.abs(z - lastZ);
Copy the code
There is no difficulty in the function behind, if you do not understand the place, the knowledge involved in the beginning of the article has been involved, here is to do compatible with IOS (multiple versions) and android ID processing, coupled with the anti-shake function, so that the shake function is more efficient
The complete code is shown below
<body>
<button id="btn">Turn it on and shake it</button>
<button id="stopBtn">Close and shake</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 // what to do when shaking shake shake:fn // what to do when shaking end: fn// what to do when shaking end} */
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; // Cancel shake listener
}
let unShake;
btn.addEventListener("touchend".() = > {
unShake = setShake({
start:() = >{
info.innerHTML += "Start shaking it
";
},
shake:() = >{
info.innerHTML += "Shake it up
";
},
end: () = >{
info.innerHTML += "Shake it off
"; }})}); stopBtn.addEventListener("touchend".() = >{
if(unShake){ unShake(); }})</script>
</body>
Copy the code
motion.js
// In IOS 12, determine whether the user has disabled action and direction access
{
let timer = setTimeout(() = > {
alert("Please enable action and direction access, otherwise you will not be able to use this application");
}, 200);
window.addEventListener("devicemotion".() = > {
clearTimeout(timer);
}, { once: true });
}
// Check whether the current system is Android
function isAndroid() {
const u = window.navigator.userAgent;
return u.indexOf("Android") > -1 || u.indexOf("Adr") > -1;
}
/* setMotion set to listen for acceleration changes to handle cb acceleration changes to do after the return cancel event registration */
function setMotion(cb) {
let fn = (e) = > {
if (isAndroid()) { // Handle the android inversion problem
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);
};
// Differentiate between IOS 13 and pre-ios
if (typeof DeviceMotionEvent.requestPermission === "function") { // IOS 13 and later
DeviceMotionEvent.requestPermission()
.then(permissionState= > {
if (permissionState === 'granted') {
// Permission allowed
window.addEventListener("devicemotion", fn);
}
}).catch(() = > {
alert("Please enable authorization or you cannot use this application."); })}else { // Before Android and IOS 13
window.addEventListener("devicemotion", fn)
}
return () = >{
window.removeEventListener("devicemotion",fn); }}Copy the code
Fourth, concluding remarks
If you find this article helpful, you can reach out your small hands and give this article a thumbs up
I am a new front end the way of new, with the attitude of learning, with meet you companion’s point of view, to share his knowledge, in addition to let oneself more consolidation of knowledge, also hope everyone can learn through the article I wrote a little knowledge, if any error is knowledge content, can be in the comments section below or the public, tell me, I will immediately change
Finally, I also created a “front-end harvester” public account, I hope you can pay attention to a wave of articles are hair loss