Web

web中的移动端的Touch事件和手动实现侧边栏

2019-11-08  本文已影响0人  追逐_chase
web.jpeg

Touch事件

 let oDiv = document.querySelector("div");

    oDiv.addEventListener("touchstart",function () {

        console.log("开始触摸");
    });

    oDiv.addEventListener("touchmove",function () {

        console.log("移动一手指");
    });

    oDiv.addEventListener("touchend",function () {

        console.log("结束触摸---")
    });

Touch事件对象

 let oDiv = document.querySelector("div");
    oDiv.addEventListener("touchstart",function (e) {
        e = e || window.event;
        console.log(e);
    })
image.png

我们经常使用的就是 touches,targetToucheschangedTouches
touches:页面上(屏幕)所有的触目点
targetTouches:元素(容器盒子)上的触摸点,当touchend执行的时候,event事件里面的 targetTouches不在有触摸点对象
changedTouches:当前屏幕上刚刚接触的手指或者离开的手指
区别:
touches和targetTouches
1.如果都是将手指按到了同一个元素上, 那么这两个对象中保存的内容是一样的
2.如果是将手指按到了不同的元素上, 那么这个两个对象中保存的内容不一样
3.touches保存的是所有元素中的手指, 而targetTouches保存的是当前元素中的手指

Touch事件对象中的clientX,pageX,screenX

  <style>
        *{
            padding: 0;
            margin: 0;
        }

        div{
            width: 600px;
            height: 100px;
            background: linear-gradient(to right, red, green);
            margin-left: 50px;
            margin-top: 50px;
        }

    </style>


<script>
  
    let oDiv = document.querySelector("div");

    oDiv.addEventListener("touchstart",function (event) {

        event = event || window.event;

        console.log(event.targetTouches);
        let obj = event.targetTouches[0];
        console.log( "clientX:"+ obj.clientX,"clientY:"+obj.clientY);
        console.log("pageX:"+obj.pageX,"pageY:"+obj.pageY );
        console.log("screenX:"+obj.screenX,"screenX:"+obj.screenY );

        /*
        screenX:是参照 页面/屏幕 左上角的
        clientX:参考点是 可视区域的 左上角
        pageX: 是参考 内容的左上角 (内容过宽 超出屏幕)
         */

        /*


    });

</script>


下图 可以更改的解释clientX,pageX,screenX
1.红色代表screenX
2.黄色代表pageX,蓝色的长方格是 内容区域的超出了手机屏幕
3.手绘的蓝色是clientX
也可以参考JS操作html元素中clientX、offsetX、screenX、pageX的区别一文的概述

image.png

Touch事件的点透问题

注意粉色盒子消失,log的打印情况

点透问题.gif
<script type="text/javascript">

    // 绿色盒子 和  粉色盒子 是兄弟关系
    
    let oBox = document.querySelector(".box");
    let oTap = document.querySelector(".tap");

    //绿色盒子
    oBox.addEventListener("click",function () {
        console.log("-------");
    });

    //粉色盒子
    oTap.addEventListener("touchstart",function () {
        oTap.style.display = "none";
    });

</script>

需要提醒的是:这种情况不是 事件冒泡,因为2个盒子是兄弟关系,不是父子关系

出现这种情况的原因是什么呢?
原因是

1.当我们手指触摸屏幕的时候,系统会生成2个事件,一个是touch事件,一个是click事件, touch事件优先click事件执行, click事件相对会延迟 100-300毫秒

2.当touch事件执行的时候pink粉色盒子的display属性更改为none粉色盒子消失,而click事件在200毫秒之后,执行的时候,发现粉色盒子已经消失,那么click事件就会执行 对应的触摸点位置的box盒子,进而触发box盒子的click事件

如何解决?

这个解决其实就是 阻止事件扩散其实就是阻止默认事件的执行 event.preventDefault(),所以需要在 粉色盒子执行touch 和 click事件时加上

 //粉色盒子
    oTap.addEventListener("touchstart",function (event) {
        event = event || window.event;
        oTap.style.display = "none";
        //阻止事件的默认行为
        event.preventDefault();
    });

其他的一些解决方法 第三方插件 比如:Fastclick专门用来解决这个问题,Zepto插件,注意早期的Zepto没有处理这个点透问题

关于 移动端点击事件的封装

点击事件的特点
1.按下和离开时间不能太久 100 - 200ms
2.按下和离开距离不能(不能移动)太远 5px
3.单根手指

 function Tap(dom, fn) {
        if(!(dom instanceof HTMLElement)){
            throw new Error("请传入一个DOM元素");
        }
        let startX = 0;
        let startY = 0;
        let startTime = 0;
        dom.ontouchstart = function (event) {
            // 1.判断当前元素中有几根手指
            if(event.targetTouches.length > 1){
                return;
            }
            // 2.拿到手指按下的位置
            startX = event.targetTouches[0].clientX;
            startY = event.targetTouches[0].clientY;
            // 3.拿到手指按下的时间
            startTime = Date.now();
        }
        dom.ontouchend = function (event) {
            // 1.判断有几根手指离开了
            if(event.changedTouches.length > 1){
                return;
            }
            // 2.拿到离开手指的位置
            let endX = event.changedTouches[0].clientX;
            let endY = event.changedTouches[0].clientY;
            // 3.判断手指离开的位置和按下位置的距离
            if(Math.abs(endX - startX) > 5 ||
                Math.abs(endY - startY) > 5){
                return;
            }
            // 4.拿到手指离开的时间
            let endTime = Date.now();
            // 5.判断手指离开的时间和按下的时间
            if(endTime - startTime > 100){
                return;
            }
            // console.log("单击事件");
            fn && fn();
        }
    }
//封装 tap事件
    function tap(element,callBack) {
        //记录刚开始的触摸时间
        let startTime = null;
        //记录结束的时间
        let endTime = null;
        //是否移动的标志
        let isMove = false;

        //触摸开始
        element.addEventListener("touchstart",function (e) {
            //获取当前的时间戳
            startTime = Date.now();
        });

        //移动
        element.addEventListener("touchmove",function (e) {
            //移动了
            isMove = true;
        });

        //结束
        element.addEventListener("touchend",function (e) {
            //获取结束的时间戳
            endTime = Date.now();

            if (endTime - startTime <= 200 && isMove === false){
                if (callBack){
                    //触摸对象返回给 回调函数
                    callBack(e);
                }

            }
            // 还原初始状态
            isMove = false;
        });

    }


手动实现侧边栏

原理:1.手指触摸屏幕 开始startY 记录位置 2.手指移动记录位置endy,其结束的偏移量offsetY = endY - startY 需要注意的是: 因为你第一次和 第二次 offsetY值不同,需要一个记录变量currentY保存上次的offsetY,这样在下次移动的时候,就是在上一次的基础上 滚动

<style>
        *{
            padding: 0;
            margin: 0;
            touch-action: none;
        }
        html,body{
            width: 100%;
            height: 100%;
        }
        ul {
            list-style: none;
        }
        a{
            text-decoration: none;
        }
        .box{
            display: inline-block;
            width: 95px;
            border: 1px solid;
            position: relative;
            overflow: hidden;
            /*高度100% 就是手机屏幕的 高度*/
            /*不设置的话 ul撑开div 高度和ul的一样了*/
            height: 100%;

        }
        .box > ul {
            width: 100%;
            position: relative;
        }
        .box > ul > li {
            height: 44px;
            line-height: 44px;
            width: 100%;
            border-bottom: 1px solid;
            text-align: center;
        }


    </style>
image.png
<script type="text/javascript">

    let oDiv = document.querySelector(".box");
    let oUl = document.querySelector("ul");

    console.log(oDiv.offsetHeight,oUl.offsetHeight);

    let startY = 0;
    //移动的位置
    let offsetY = 0;
    //保存上一次移动的位置
    let currentY = 0;
    //定义的缓冲量
    let buffer = 100;

    let maxOffsetY = 0;
    //向上移动 Y值是 负数
    let minOffsetY = - (oUl.offsetHeight - oDiv.offsetHeight);

    console.log("minOffsetY:",minOffsetY);

    oUl.addEventListener("touchstart",function (e) {
        //获取开始的位置
        startY = e.targetTouches[0].clientY;

    });

    //移动
    oUl.addEventListener("touchmove",function (e) {
        let endY = e.targetTouches[0].clientY;
        offsetY = endY - startY;
        //当上一次currentY的便宜 + 现在的offsetY 偏移量 > 极限缓冲的时候 不能在向下移动
        if ((currentY + offsetY) >= (maxOffsetY + buffer)){
            return;
        }
        if ((currentY + offsetY) <= (minOffsetY - buffer)){
            return;
        }


        this.style.top = currentY + offsetY + "px";

    });

    //触摸结束
    oUl.addEventListener("touchend",function (e) {
        //当手指 离开 屏幕的时候 记录 上一次 移动的位置
        currentY += offsetY;
        //当向下移动的距离 大于等于 最大的极限值时
        if (currentY >= maxOffsetY){
            currentY = maxOffsetY;
            //复位
            // this.style.top = currentY + "px";
        } else if (currentY < minOffsetY ){
            currentY = minOffsetY;
            //复位
            // this.style.top = currentY + "px";
        }

        console.log(currentY,minOffsetY);

        //复位
        this.style.top = currentY + "px";

    });

</script>

上一篇下一篇

猜你喜欢

热点阅读