【移动端】deviceMotion事件

1,826 阅读3分钟

横竖屏切换检测

事件名:orientationchange

属性:window.orientation

值:90|-90:竖屏 0|180:横屏

function setOrientation() {
      switch (window.orientation) {
        case 90:
        case -90:
          alert("竖屏");
          break;
        default:
          console.log("横屏");
          break;
      }
}
setOrientation();
window.addEventListener("orientationchange",setOrientation)

手机加速度检测

属性: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)}
          `;
      });

体感操作

(IOS系统)

let box = document.querySelector("#box");
      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;
          /*x=-x;y=-y;(Android系统需加上)**/
          translateX += x;
          translateY -= y;
          box.style.transform = `translate(${translateX}px,${translateY}px)`;
      });

加速检测系统存在的问题

  1. 安卓下和IOS的差异

    安卓下 和 IOS 下,加速度方向取值相反:如 IOS x = 10,安卓 x = -10;IOS y = 10,安卓 y = -10,IOS z = 10,安卓 z = -10

  2. 协议问题

    IOS 中,如果要使用加速度 API,当前应用则必须使用 https 协议

  3. IOS12.2

    IOS 12.2 中,用户可以在手机设置中关闭掉,动作与方向的访问权限

  4. IOS13

    IOS 13 及之后,当应用中想要使用动作与方向的访问权限时,需要请求用户授权。

  5. IOS13.3

    IOS 13.3 及之后,申请授权,必须用户手动触发。 统一解决

// 在 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);
  }
}

防抖和节流

防抖

隔一段事件执行,希望函数只执行一次,哪怕进行了多次调用

/*
     debounce 防抖函数
     fn:需要进行防抖的函数
     deley: 防抖时长
     start: 是否在开始时执行函数
     return 经过防抖处理的函数
*/
function debounce(fn, deley = 200, start = false) {
      let timer;
      return function (...args) { // 经过防抖处理的函数
        const _this = this;
        if(timer){
          clearTimeout(timer);
        }
        start && fn.apply(_this, args);  
        timer = setTimeout(() => {
          (!start) && fn.apply(_this, args);
        }, deley);
      }
    }

节流

减少执行频率,让函数保持在一个可接受的固定频率执行

/**
     * 节流
     * fn:
     * dalay:
     * start:
     * return
*/
function throttle(fn,deley = 200,start = true) {
          let timer;
          return function(...args) { // 经过防抖处理的函数
            const _this = this;
            if(timer){
              return;
            }
            start&&fn.apply(_this,arg);
            timer = setTimeout(() => {
              (!start)&&fn.apply(_this,args);
              timer = null;
            }, deley);
          }
      }
function throttle(fn, deley = 200, start = true) {
      let startTime = Date.now();
      return function (...args) {
        const _this = this;
        if (Date.now() - startTime < deley) {
          return;
        }
        startTime = Date.now();
        start && fn.apply(_this, args);
      }
    }

写一个摇一摇

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

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Document</title>
</head>

<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");
    /*
      摇一摇:当前次的加速度和上一次的加速之间有了一个比较大的差值
    */
    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);
      }
    }
    /*
     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>

</html>