Using: Next.JS 14.0.4; iPhone 14
I need to detect if and how user moves his phone using React.JS (I'm using Next.JS 14.0.4). For this purpose I began looking for some libraries that can detect it with gyroscope or accelerometer and reached Shake.js , Motion sensors , and default JS event devicemotion
For every library or event I wrote a code by the example provided on corresponding site.
Some code
Shake.js:
"use client"
import React, { useEffect, useState } from 'react';
import Shake from 'shake.js';
export default function Home() {
const [shakeDetected, setShakeDetected] = useState(false);
useEffect(() => {
const shakeEvent = new Shake({ threshold: 15, timeout: 1000 });
shakeEvent.start();
window.addEventListener('shake', shakeHandler, false);
return () => {
shakeEvent.stop();
window.removeEventListener('shake', shakeHandler, false);
};
}, []);
const shakeHandler = () => {
setShakeDetected(true);
setTimeout(() => setShakeDetected(false), 5000); // Reset after 5 seconds
};
return (
<div>
<h1>Shake Detection</h1>
{shakeDetected ? <p>Shake detected!</p> : <p>Shake your device...</p>}
</div>
);
}
Motion sensors:
"use client";
import { useEffect, useState } from "react";
import styles from "./page.module.css";
import { Accelerometer } from "motion-sensors-polyfill";
export default function Home() {
const [accelerationCoords, setAccelerationCoords] = useState({
x: 0,
y: 0,
z: 0,
});
useEffect(() => {
const sensor = new Accelerometer();
sensor.addEventListener("reading", () => {
setAccelerationCoords({
x: sensor.x,
y: sensor.y,
z: sensor.z,
});
});
sensor.start();
return () => {
sensor.stop();
};
}, []);
return <main className={styles.main}>
<p>{accelerationCoords.x}</p>
<p>{accelerationCoords.y}</p>
<p>{accelerationCoords.z}</p>
</main>;
}
devicemotion:
import React, { useEffect, useState } from 'react';
export default function Home() {
const [motion, setMotion] = useState({ x: 0, y: 0, z: 0 });
useEffect(() => {
function handleDeviceMotion(event) {
setMotion({
x: event.accelerationIncludingGravity.x,
y: event.accelerationIncludingGravity.y,
z: event.accelerationIncludingGravity.z,
});
}
window.addEventListener('devicemotion', handleDeviceMotion);
return () => {
window.removeEventListener('devicemotion', handleDeviceMotion);
};
}, []);
return (
<div>
<h1>Device Motion</h1>
<p>X: {motion.x}</p>
<p>Y: {motion.y}</p>
<p>Z: {motion.z}</p>
</div>
);
}
Then I started host with "devip": "next dev -H 0.0.0.0 -p 8080" (I insert my ip instead 0.0.0.0) and then connect to the host both from desktop browser and from phone (tried both Safari and Chrome).
Result
No matter how much I shake my phone completely nothing happens. I tried to change setState in each block manually like:
setShakeDetected(true);
inside each event listener and it does not change this state
-
This question is similar to: How do I get DeviceOrientationEvent and DeviceMotionEvent to work on Safari?. If you believe it’s different, please edit the question, make it clear how it’s different and/or how the answers on that question are not helpful for your problem.el cato– el cato2024年09月03日 13:28:46 +00:00Commented Sep 3, 2024 at 13:28
2 Answers 2
Note that Shake.js has been archived in 2020 so it seems that is not being actively maintained.
That being said, Shake.js relies on devicemotion under the hood so this applies both to your Shake.js and the devicemotion approaches.
From the devicemotion MDN documentation:
This feature is available only in secure contexts (HTTPS), in some or all supporting browsers.
By default, nextjs runs the project under http. If that's your case, the event is not being triggered because of that.
To run your project in https you will have to change your script in package.json file, so that it includes the --experimental-https flag:
next dev --experimental-https
After that your code should work as it is.
Note that this solution is handy for development purposes. For production you should generate your own issued certificates.
Comments
The answer by @mgarcia is only part of the picture. For Safari on iOS devices, asides from running in a secured (HTTPS) context, you must also request necessary permission through DeviceMotionEvent.requestPermission() method.
Notice that this won't work if you call it on page loads. As this is already answered in other thread, you can refer to other answers under this SO question for code examples.
Comments
Explore related questions
See similar questions with these tags.