H5 device motion event DeviceMotionEvent realizes the shake function

1. Introduction to DeviceMotion

There is a special event devicemotion in the window object, which encapsulates the event of the motion sensor data and can be used to monitor the acceleration change of the device and other information. The devicemotion event object has four attributes: acceleration, accelerationIncludingGravity, rotationRate and interval

    window.addEventListener("devicemotion", (e) => {
      console.log(e)
    });

[1] acceleration (acceleration), accelerationIncludingGravity (acceleration of gravity)

The acceleration and accelerationIncludingGravity attributes both contain three axes: X axis, Y axis, and Z axis

  • acceleration: Provides the object for the acceleration of the device in the X, Y, and Z axis directions. The unit of acceleration is  m/s2
  • accelarationIncludingGravity: Provides an object with the acceleration of the device in the X, Y, and Z axis directions with gravity. The unit of acceleration is  m/s2

Insert picture description here

    window.addEventListener("devicemotion", (e) => {
        let { acceleration, accelerationIncludingGravity } = e;
        
        let { x, y, z } = acceleration; //手机目前的移动速度 -- 加速度
        console.log(x,y,z);

        let { x:x2, y:y2, z:z2 } = acceleration; // 检测手机的移动速度 和 现在每个方向受到的重力加速度
        console.log(x2,y2,z2);
    });

[2] rotationRate (rotation speed)

Provides an object of the rate of rotation of the device in the alpha, beta, and gamma axis directions. The unit of rotation rate is degrees per second

  • alpha: the rotation rate of the device along the axis vertical to the screen
  • beta: The rotation rate of the device along the left-to-right axis of the screen
  • gamma: The rotation rate of the device along the bottom-to-up axis of the screen

[3] interval (time interval)

Indicates the interval time for obtaining data from the device, in milliseconds

2. Precautions for using DeviceMotion

[1] When using acceleration, the value under IOS is just the opposite of the value under Android

Android phones: move to the left, the x-axis will get a speed of 20; move to the right, the x-axis will get a speed of -20

IOS mobile phone: move to the left, the x-axis will get a speed value of -20; move to the right, the x-axis will get a speed value of 20

The Y-axis and Z-axis are the same, so our code needs to be processed and compatible with Android phones and IOS phones.

    // 判断是否是IOS设备,true代表IOS,false代表非IOS
    function getIos() {
      let u = window.navigator.userAgent;
      return !!u.match(/\(i[^;]+;( U;)? CPU.+Mac OS X/);
    }

    window.addEventListener("devicemotion", (e) => {
      let { acceleration } = e;
      let { x, y, z } = acceleration;
      // 如果是IOS设备,xyz取反
      if (getIos()) {
        x = -x;
        y = -y;
        z = -z;
      }
    });

[2] https requirements under IOS

After ios 11 (including some current Android machines), if you want to use some events related to the gyroscope on the page, you must use the https protocol

[3] IOS users turned off "Action and Direction Access"

After IOS 12.2, users can turn off "Action and Direction Access" in the settings of the phone, as shown in the figure below. So far, is there an effective way for developers to directly obtain whether the user has turned off "action and direction access"

Solution: We use window.DeviceMotionEvent to exclude devices that do not support acceleration. If the device supports it, it will have the property window.DeviceMotionEvent. If the device supports window.DeviceMotionEvent, start an event monitoring (note that the acceleration acquisition is particularly sensitive, even if we think that our mobile phone is stationary, there will be some acceleration to it, so the devicemotion event will be triggered from time to time, and the trigger interval is very small), If the event is not executed, it means that the user has turned off the action and direction permissions.

    if (!window.DeviceMotionEvent) {
      alert("您的设备不支持DeviceMotion");
    } else {
      let timer = setTimeout(function () {
        alert('请在设置中开启您设备的"动作和方向访问"权限,否则您将无法使用本应用');
      }, 100);
      window.addEventListener("devicemotion", (e) => {
        clearTimeout(timer);
      }, { once: true });
    }

[4] IOS 13 user permission request

A new method after IOS 13 is used to obtain user permissions. After obtaining user permissions, we can detect acceleration, but note that this method is only available after IOS 13. If it is written like this before IOS 13, under DeviceMotionEvent If there is no requestPermission method, an error will be reported

     DeviceMotionEvent.requestPermission().then(permissionState => {
        if (permissionState === 'granted') {
          window.addEventListener('devicemotion', () => { })
        }
      }).catch((err) => {

      });

3. Realization of shake function

[1] Implementation principle: When we shake the phone, the phone itself will generate an acceleration. We can intercept the two accelerations of the mobile phone for comparison, and there will be a difference in the middle. When the difference is greater than a certain range, it can be assumed that the user has performed the operation of shaking.

[2] Note:

  1. We are not sure which direction the user has forgotten to shake, so we need to detect the X-axis, Y-axis, Z-axis, three directions
  2.  Shake this function generally we do certain things after the user stops shaking
  3. The execution interval of devicemotion is extremely small, which can reach a few millimeters, but in terms of performance, we do not need such a small execution interval, so we add a throttle function
  4. The compatibility processing mentioned above needs to be added

[3] Code

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>Document</title>
</head>

<body>
  <button id="startBtn">开启摇一摇</button>
  <button id="closeBtn">关闭摇一摇</button>
  <script>
    /*
      setDeviceMotion 添加陀螺仪监控
      参数: cb devicemotion的事件处理函数; errCb 不支持devicemotion时的处理回调
    */
    function setDeviceMotion(cb, errCb) {
      if (!window.DeviceMotionEvent) {
        errCb("设备不支持DeviceMotion");
        return;
      }
      if (typeof DeviceMotionEvent.requestPermission === 'function') {
        // IOS13 设备
        DeviceMotionEvent.requestPermission()
          .then(permissionState => {
            if (permissionState === 'granted') {
              window.addEventListener('devicemotion', cb);
            }
          })
          .catch((err) => {
            errCb("用户未允许权限");
          });
      } else {
        // 其他支持加速度检测的系统
        let timer = setTimeout(function () {
          errCb("用户未开启权限");
        }, 1000);
        window.addEventListener("devicemotion", (e) => {
          clearTimeout(timer);
        }, { once: true });
        window.addEventListener("devicemotion", cb);
      }
    }


    /*
        throttle 节流函数
        参数:
            fn 要节流的函数
            interval 节流间隔时间
            start 是否在节流开始时执行 (true在开始时执行,false在结束时执行)
        返回值:
            经过节流处理的函数
    */
    function throttle(fn, interval = 200, start = true) {
      if (typeof fn !== "function") {
        return console.error("请传入一个函数");
      }
      let timer = 0;
      return function (...arg) {
        let _this = this;
        if (timer) {
          return;
        }
        start && fn.apply(_this, arg);
        timer = setTimeout(() => {
          (!start) && fn.apply(_this, arg);
          timer = 0;
        }, interval);
      }
    }


    /*
        addShake 添加摇一摇功能
        参数:cbShake 类型 fn 当用户进行了摇一摇之后要做的事情
        返回值:shakeIndex 开启的第几个摇一摇功能的索引,用来删除监听     
    */
    function addShake(cbShake) {
      const maxRange = 60; //当用户的两次加速度差值大于这个幅度,判定用户进行了摇一摇功能
      const minRange = 10; //当用户的两次加速度差值小于这个幅度,判定用户停止摇动手机
      let isShake = false; //记录用户是否摇动手机
      let lastX = 0,
          lastY = 0,
          lastZ = 0;
      function toShake(e) {
        let motion = e.acceleration;
        let { x, y, z } = motion;
        let range = Math.abs(x - lastX) + Math.abs(y - lastY) + Math.abs(z - lastZ);
        if (range > maxRange) {//用户进行了摇一摇
          isShake = true;
        }
        if (range < minRange && isShake) { // 停止摇一摇
          cbShake(e);
          isShake = false;
        }
        lastX = x;
        lastY = y;
        lastZ = z;
      }
      if (!window.shakeEvent) { //建立 shakeEvent 存储所有的摇一摇的处理函数,方便一会取消
        window.shakeEvent = [];
      }
      toShake = throttle(toShake);
      window.shakeEvent.push(toShake);
      setDeviceMotion(toShake, (errMessage) => {
        alert(errMessage)
      })
      return window.shakeEvent.length - 1;//返回该次摇一摇处理的索引
    }


    /*
        删除摇一摇监听
        cbShake 类型 fn 当用户进行了摇一摇之后要做的事情
    */
    function remveShake(shakeIndex) {
      window.removeEventListener("devicemotion", window.shakeEvent[shakeIndex]);
    }

    // 调用摇一摇
    {
      let startBtn = document.querySelector("#startBtn");
      let closeBtn = document.querySelector("#closeBtn");
      let isStartShake = false;
      let shakeIndex;
      // 再次强调 IOS 13.3 需要用户触发,再能开启摇一摇 
      startBtn.addEventListener("touchend", () => {
        if (isStartShake) return;
        isStartShake = true;
        shakeIndex = addShake(() => {
          alert("您进行了摇一摇")
        })
      });
      closeBtn.addEventListener("touchend", () => {
        if (!isStartShake) return;
        isStartShake = false;
        remveShake(shakeIndex);
      });

    }
  </script>
</body>

</html>

Articles are continuously updated every week. You can search for " Front-end Collection  " on WeChat to  read it for the first time, and reply to [ Video ] [ Book ] to receive 200G video materials and 30 PDF book materials

 

Guess you like

Origin blog.csdn.net/qq_38128179/article/details/114304720