Lvasche
Lvasche

Reputation: 529

Detect mobile device movements

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

Upvotes: 5

Views: 491

Answers (2)

el cato
el cato

Reputation: 81

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.

Upvotes: 1

mgarcia
mgarcia

Reputation: 6325

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.

Upvotes: 6

Related Questions