最新公告
  • 欢迎您光临起源地模板网,本站秉承服务宗旨 履行“站长”责任,销售只是起点 服务永无止境!立即加入钻石VIP
  • 实现移动端摇一摇功能(内含移动端知识)

    正文概述 掘金(邑大学子_)   2021-02-10   663

    实现移动端摇一摇功能(内含移动端知识)

    一、devicemotion事件

    在实现移动端摇一摇功能前,需要大家先了解移动端的devicemotion事件,以及防抖节流的知识,这样有助于大家理解

    监控横竖屏切换检测

    监听orientationchange时间,通过window.orientation属性判断当前屏幕属于横屏(90,-90)还是竖屏(0,180)

    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)
    

    手机加速度检测

    主要监听devicemotion事件,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)}
            `;
    });
    

    手机收到的重力

    前面我们讲到accelerationIncludingGravity为手机加速度+重力,acceleration为加速度,则两者相减就能得到重力

    window.addEventListener("devicemotion",(e)=>{
        const motion = e.accelerationIncludingGravity; // 手机加速度 + 重力
        const motion2 = e.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)}
            `;
    });
    

    体感操作

    利用translateXtranslateY

    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;
        translateX += x;
        translateY -= y;
        box.style.transform = `translate(${translateX}px,${translateY}px)`;
    });
    

    踩坑

    加速度在检测过程中,会存在一些坑:

    • 安卓下 和 IOS 下,加速度方向取值相反

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

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

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

    • IOS 13.3 及之后,申请授权,必须用户手动触发

    针对上述的坑,我们来一步步解决

    由于在安卓下 和 IOS 下,加速度方向取值相反,我们可以编写一个方法判断是否为安卓

    function isAndroid() {
        const u = window.navigator.userAgent;
        return u.indexOf("Android")>-1||u.indexOf("Adr")>-1;
    }
    

    IOS 12.2 中,如果用户在在手机设置中设置关闭了动作与方向的访问权限,那么我们就监听不了devicemotion事件

    我们可以在页面一开始渲染的时候就触发一个延时器提示用户设置权限,再通过监听事件,清楚这个延时器

    let timer = setTimeout(() => {
        alert("请开启动作与方向的访问权限,否则将无法使用本应用");
    }, 200);
    window.addEventListener("devicemotion",()=>{
        clearTimeout(timer);
    },{once:true});
    

    IOS 13.3 及之后,申请授权,必须用户手动触发。这里的意思大概就是,我们不能把加速度的监听事件,直接在挂载的时候渲染。需要在DOM事件触发后执行

    btn.addEventListener("touchend",()=>{
        setMotion((e) => {
            const motion = e.accelerationIncludingGravity; // 手机加速度 + 重力
            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)`;
        });
    });
    

    二、防抖与节流

    防抖

    简单来讲,就是希望函数只执行一次,哪怕我进行了多次调用

    下面给出一个简单版本的防抖函数封装:

    其中,fn为需要防抖的函数,delay为防抖时长,start为是否在开始时执行函数,return返回经过近防抖处理的函数

    防抖主要需要处理的两点地方:

    • 处理this指向问题
    • 处理参数问题
    function debounce(fn,deley = 200,start = false) {
        let timer = 0;
        let isStart = true;
        return function(...arg) { // 经过防抖处理的函数
            const _this = this;
            clearTimeout(timer);
            if(isStart){
                start && fn.apply(_this,arg);
                isStart = false;
            }
            timer = setTimeout(() => {
                (!start)&&fn.apply(_this,arg);
                isStart = true;
            }, deley);
        }
    }
    

    节流

    简单来讲,让函数保持在一个可接受的固定频率执行

    节流跟防抖十分的相似,无非就是判断的时候处理方式不同

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

    三、实现过程

    实现摇一摇的本质就是:当前次的加速度和上一次的加速之间有了一个比较大的差值

    下面定义两个属性来判断

    const maxRange = 50; // 当加速度的差值大于该值时认定用户进行了摇一摇
    const minRange = 5; // 当加速度的差值小于于该值时认定用户停止了摇一摇
    

    获取手机加速度

    const { x, y, z } = e.acceleration;
    const range = Math.abs(x - lastX) + Math.abs(y - lastY) + Math.abs(z - lastZ);
    

    后面的功能就没什么难点,如果看到不懂的地方,涉及的知识点文章开头就已经涉及,这里是做了兼容 IOS(多个版本) 与 Anroid 的处理,再加上防抖功能,让摇一摇功能更加高效

    下面给出完整代码

    <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");
            /*
         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>
    

    motion.js

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

    四、结束语

    如果觉得这篇文章对你有帮助,可以伸出你的小手,为这篇文章点个赞

    我是前端路上一位新晋的萌新,怀着学习的态度,怀着认识各位同伴的心态,把自己的知识分享出来,除了让自己对知识认知更加巩固,也希望大家能够通过我写的文章学到一点微薄的知识,如果知识内容有误,可以在评论区或者下面公众号告诉我,我会立刻更改

    最后,我也创建了一个 【前端收割机】的公众号,希望大家可以关注一波,里面的文章都是掉头发之作


    起源地下载网 » 实现移动端摇一摇功能(内含移动端知识)

    常见问题FAQ

    免费下载或者VIP会员专享资源能否直接商用?
    本站所有资源版权均属于原作者所有,这里所提供资源均只能用于参考学习用,请勿直接商用。若由于商用引起版权纠纷,一切责任均由使用者承担。更多说明请参考 VIP介绍。
    提示下载完但解压或打开不了?
    最常见的情况是下载不完整: 可对比下载完压缩包的与网盘上的容量,若小于网盘提示的容量则是这个原因。这是浏览器下载的bug,建议用百度网盘软件或迅雷下载。若排除这种情况,可在对应资源底部留言,或 联络我们.。
    找不到素材资源介绍文章里的示例图片?
    对于PPT,KEY,Mockups,APP,网页模版等类型的素材,文章内用于介绍的图片通常并不包含在对应可供下载素材包内。这些相关商业图片需另外购买,且本站不负责(也没有办法)找到出处。 同样地一些字体文件也是这种情况,但部分素材会在素材包内有一份字体下载链接清单。
    模板不会安装或需要功能定制以及二次开发?
    请QQ联系我们

    发表评论

    还没有评论,快来抢沙发吧!

    如需帝国cms功能定制以及二次开发请联系我们

    联系作者

    请选择支付方式

    ×
    迅虎支付宝
    迅虎微信
    支付宝当面付
    余额支付
    ×
    微信扫码支付 0 元