威尼斯手机平台登陆-官方网站登录

威尼斯手机平台登陆为您带来世界三大博彩公司最新相关资讯,威尼斯官方网站登录充分考虑到不同地域网民的不同需求,威尼斯手机平台登陆良好的用户界面,人性化的操作,实用的功能设计使其广泛受到欢迎,推动实体出版、影视、动漫、游戏等相关文化产业的发展。

您的位置:威尼斯手机平台登陆 > 前端资源 > 威尼斯登录首页关于移动手势检测我们这里不再赘述,作为腾讯手机QQ

威尼斯登录首页关于移动手势检测我们这里不再赘述,作为腾讯手机QQ

发布时间:2020-04-21 17:03编辑:前端资源浏览(132)

    前言

    随着 Hybrid 应用的丰富,HTML5 工程师们已经不满足于把桌面端体验简单移植到移动端,他们觊觎移动原生应用人性化的操作体验,特别是原生应用与生俱来的丰富的手势系统。HTML5 没有提供开箱即用的手势系统,但是提供了更底层一些的对 touch 事件的监听。基于此,我们可以做出自己的手势库。

    目前AlloyFinger作为腾讯手机QQ web手势解决方案,在各大项目中都发挥着作用。
    感兴趣的同学可以去Github看看:

    在前端的移动Web开发中,有一部分事件只在移动端产生,如触摸相关的事件。接下来给大家简单总结一下移动端的事件。

    手势

    常用的 HTML5 手势可以分为两类,单点手势和两点手势。单点手势有 tap(单击),double tap(双击),long tap(长按),swipe(挥),move(移动)。两点手势有 pinch(缩放),rotate(旋转)。

    接下来我们实现一个检测这些手势的 javaScript 库,并利用这个手势库做出炫酷的交互效果。

    威尼斯登录首页 1

    在腾讯,如:兴趣部落、QQ群、QQ动漫、腾讯学院、TEDxTencent、 AlloyTeam、腾讯CDC等多个部门、团队和项目都在使用AlloyFinger。如下图所示:

    1. PC端事件在移动端的兼容问题

    移动

    关于移动手势检测我们这里不再赘述。总结一下就是在每次touchmove事件发生时,把两个位移点之间的坐标位置相减,就可以了。

    威尼斯登录首页 2

    1.1 click事件的200~300ms延迟问题

    由于移动端默认的布局视口宽度是980像素,所以网页文字非常小,为了快速让网页还原到原来的大小,Safari最新引入了双击缩放功能:用户双击手机页面的时候,浏览器会智能的缩放当前页面到原始大小。

    ​双击缩放的原理就是,当用户click一次之后,浏览器会经过约300ms之后检测是否再有一次click,如果有的话,就会缩放页面。否则的话就是一个click事件。

    由于双击缩放功能存在,click事件触发就会有大约200~300ms的延迟。

    单击(tap)

    手势检测的关键是用 touchstart,touchmove,touchend 三个事件对手势进行分解。

    那么怎么分解单击事件呢?

    1. 在 touchstart 发生时进入单击检测,只有一个接触点。因为单击事件限制为一个手指的动作。
    2. 没有发生 touchmove 事件或者 touchmove 在一个很小的范围(如下图)。限制 touchmove 在一个很小范围,是为了给用户一定的冗余空间,因为不能保证用户手指在接触屏幕的时候不发生轻微的位移。

    威尼斯登录首页 3

    3.touchend 发生在 touchstart后的很短时间内(如下图)。这个时间段的阈值是毫秒级,用来限制手指和屏幕接触的时间。因为单击事件从开始到结束是很快的。
    威尼斯登录首页 4

    有了上面的流程,就可以开始实现 tap 事件监测了。

    _getTime() {
    
      return new Date().getTime(); 
    
    }
    
    _onTouchStart(e) {
    
        //记录touch开始的位置
    
        this.startX = e.touches[0].pageX;
    
        this.startY = e.touches[0].pageY;
    
        if(e.touches.length > 1) {
    
          //多点监测
    
          ...
    
        }else {
    
          //记录touch开始的时间
    
          this.startTime = this._getTime();
    
        }
    
     }
    
    _onTouchMove(e) {
    
      ...
    
      //记录手指移动的位置
    
      this.moveX = e.touches[0].pageX;
    
      this.moveY = e.touches[0].pageY;
    
      ...
    
    }
    
    _onTouchEnd(e) {
    
      let timestamp = this._getTime();
    
      if(this.moveX !== null && Math.abs(this.moveX - this.startX) > 10 ||
    
        this.moveY !== null && Math.abs(this.moveY - this.startY) > 10) {
    
          ...
    
      }else {
    
        //手指移动的位移要小于10像素并且手指和屏幕的接触时间要短语500毫秒
    
        if(timestamp - this.startTime < 500) {
    
          this._emitEvent('onTap')
    
        }
    
      }
    
    }
    

    基本上只要有图像裁剪、图像查看的地方都会使用到AlloyFinger。因此AlloyFinger也入选了腾讯code平台的精品组件:

    1.2 dblclick事件失效

    由于双击缩放的存在,pc端的dblclick事件也失效了。

    双击(double tap)

    和单击一样,双击事件也需要我们对手势进行量化分解。

    1. 双击事件是一个手指的行为。所以在 touchstart 时,我们要判断此时屏幕有几个接触点。
    2. 双击事件中包含两次独立的单击行为。理想情况下,这两次点击应该落在屏幕上的同一个点上。为了给用户一定的冗余空间,将两次点击的坐标点距离限制在10个像素以内。
      威尼斯登录首页 5
    3. 双击事件本质是两次快速的单击。也即是说,两次点击的间隔时间很短。通过一定的测试量化后,我们把两次单击的时间间隔设为300毫秒。
      威尼斯登录首页 6

    注意双击事件中我们检测了相邻两个 touchstart 事件的位移和时间间隔。

    _onTouchStart(e) {
    
      if(e.touches.length > 1) {
    
      ...
    
      } else {
    
        if(this.previousTouchPoint) {
    
          //两次相邻的touchstart之间距离要小于10,同时时间间隔小于300ms
    
          if( Math.abs(this.startX -this.previousTouchPoint.startX) < 10  &&
    
              Math.abs(this.startY - this.previousTouchPoint.startY) < 10 && 
    
              Math.abs(this.startTime - this.previousTouchTime) < 300) {
    
                this._emitEvent('onDoubleTap');
    
              }
    
        }
    
        //保存上一次touchstart的时间和位置信息
    
        this.previousTouchTime = this.startTime;
    
        this.previousTouchPoint = {
    
            startX : this.startX,
    
            startY : this.startY
    
         };
    
      }
    
    }
    

    威尼斯登录首页 7

    2. 移动端特有的touch事件

    由于移动端设备大都具备触摸功能,所以移动端浏览器都引入了触摸(touch)事件。

    touch相关的事件跟普通的其他dom事件一样使用,可以直接用addEventListener来监听和处理。

    最基本的touch事件包括4个事件:

    1. touchstart: 当在屏幕上按下手指时触发

    2. touchmove: 当在屏幕上移动手指时触发

    3. touchend: 当在屏幕上抬起手指时触发

    4. touchcancel 当一些更高级别的事件发生的时候(如电话接入或者弹出信息)会取消当前的touch操作,即触发touchcancel。一般会在touchcancel时暂停游戏、存档等操作。

    长按(long press)

    长按应该是最容易分解的手势。我们可以这样分解:在 touchstart 发生后的很长一段时间内,如果没有发生 touchmove 或者 touchend 事件,那么就触发长按手势。

    1. 长按是一个手指的行为,需要检测屏幕上是否只有一个接触点。
    2. 如果手指在空间上发生了移动,那么长按事件取消。
    3. 如果手指在屏幕上停留的时间超过800ms,那么触发长按手势。
    4. 如果手指在屏幕上停留的时间小于800ms,也即 touchend 在 touchstart 发生后的800ms内触发,那么长按事件取消。
      威尼斯登录首页 8
    _onTouchStart(e) {
    
      clearTimeout(this.longPressTimeout);
    
      if(e.touches.length > 1) {
    
      }else {
    
        this.longPressTimeout = setTimeout(()=>{
    
          this._emitEvent('onLongPress');
    
        });
    
      }
    
    }
    
    _onTouchMove(e) {
    
      ...
    
      clearTimeout(this.longPressTimeout);
    
      ...
    
    }
    
    _onTouchEnd(e) {
    
      ...
    
      clearTimeout(this.longPressTimeout);
    
      ...
    
    }
    

    除了国内外的项目团队都在使用AlloyFinger,国内外的各大IT网站也进行了转载报道,作为超级小的手势库,腾讯的web项目为什么不选择hammerjs而选择AlloyFinger?下面从各个角度、架构、原理上进行一下分析。

    2.1 touch事件与click事件同时触发

    在很多情况下,触摸事件和鼠标事件会同时被触发(目的是让没有对触摸设备优化的代码仍然可以在触摸设备上正常工作)。

    因为双击缩放检测的存在,在移动设备屏幕上点击操作的事件执行顺序:

    touchstart(瞬间触发) → touchend → click(200-300ms延迟)

    如果你使用了触摸事件,可以调用 event.preventDefault()来阻止鼠标事件被触发。

    缩放(pinch)

    缩放是一个非常有趣的手势,还记得第一代iPhone双指缩放图片给你带来的震撼吗?虽然如此,缩放手势的检测却相对简单。

    1. 缩放是两个手指的行为,需要检测屏幕上是否有两个接触点。
    2. 缩放比例的量化,是通过两次缩放行为之间的距离的比值得到,如下图。
      威尼斯登录首页 9

    所以缩放的核心是获取两个接触点之间的直线距离。

    //勾股定理
    
    _getDistance(xLen,yLen) {
       return Math.sqrt(xLen * xLen + yLen * yLen);
      }
    

    这里的xLen是两个接触点x坐标差的绝对值,yLen相应的就是y坐标差的绝对值。

    _onTouchStart(e) {
    
      if(e.touches.length > 1) {
    
        let point1 = e.touches[0];
    
        let point2 = e.touches[1];
    
        let xLen = Math.abs(point2.pageX - point1.pageX);
    
        let yLen = Math.abs(point2.pageY - point1.pageY);
    
        this.touchDistance = this._getDistance(xLen, yLen);
    
      } else {
    
        ...
    
      }
    
    }
    

    在_onTouchStart函数中获取并且保存 touchstart 发生时两个接触点之间的距离。

    _onTouchMove(e) {
    
      if(e.touches.length > 1) {
    
          let xLen = Math.abs(e.touches[0].pageX - e.touches[1].pageX);
    
          let yLen = Math.abs(e.touches[1].pageY - e.touches[1].pageY);
    
          let touchDistance = this._getDistance(xLen,yLen);
    
          if(this.touchDistance) {
    
            let pinchScale = touchDistance / this.touchDistance;
    
              this._emitEvent('onPinch',{scale:pinchScale - this.previousPinchScale});
    
              this.previousPinchScale = pinchScale;
    
          }
    
      }else {
    
        ...
    
      }
    
    }
    

    体积

    威尼斯登录首页 10

    可以看到hammerjs体积远远大于AlloyFinger,对于手机QQ web加载速度性能追求极致的同学来说,使用hammerjs的大小是不可以接受的!
    那么,为什么hammerjs这么大?看下架构设计便可知晓。

    2.2 touchstart事件

    ​ 当用户手指触摸到的触摸屏的时候触发。事件对象的 target 就是touch 发生位置的那个元素。

    <div>
        点击我!
    </div>
    <script>
        var box = document.querySelector("div");
        box.addEventListener("touchstart", function (e) {
           console.log('touchstart'); 
        });
    </script>
    

    旋转(rotate)

    旋转手势需要检测两个比较重要的值,一是旋转的角度,二是旋转的方向(顺时针或逆时针)。

    其中旋转角度和方向的计算需要通过向量的计算来获取,本文不再展开。

    威尼斯登录首页 11

    首先,需要获取向量的旋转方向和角度。

    //这两个方法属于向量计算,具体原理请阅读本文最后的参考文献
    
      _getRotateDirection(vector1,vector2) {
    
        return vector1.x * vector2.y - vector2.x * vector1.y;
    
      }  
    
      _getRotateAngle(vector1,vector2) {
    
        let direction = this._getRotateDirection(vector1,vector2);
    
        direction = direction > 0 ? -1 : 1;
    
        let len1 = this._getDistance(vector1.x,vector1.y);
    
        let len2 = this._getDistance(vector2.x,vector2.y);
    
        let mr = len1 * len2;
    
        if(mr === 0) return 0;
    
        let dot = vector1.x * vector2.x + vector1.y * vector2.y;
    
        let r = dot / mr;
    
        if(r > 1) r = 1;
    
        if(r < -1) r = -1;
    
        return Math.acos(r) * direction * 180 / Math.PI;
    
      }
    

    然后,我们在手指发生移动时,调用获取旋转方向和角度的方法。

    _onTouchStart(e) {
    
      ...  
    
      if(e.touches.length > 1) {
    
        this.touchVector = {
    
           x: point2.pageX - this.startX,
    
           y: point2.pageY - this.startY
    
         };
    
      }
    
      ...
    
    }
    
    _onTouchMove(e) {
    
      ...
    
      if(this.touchVector) {
    
            let vector = {
    
              x: e.touches[1].pageX - e.touches[0].pageX,
    
              y: e.touches[1].pageY - e.touches[0].pageY
    
            };
    
            let angle = this._getRotateAngle(vector,this.touchVector);
    
            this._emitEvent('onRotate',{
    
              angle
    
            });
    
            this.touchVector.x = vector.x;
    
            this.touchVector.y = vector.y;
    
          }
    
      ...
    
    }
    

    架构设计

    威尼斯登录首页 12

    威尼斯登录首页 13

    其实,hammerjs抽象出的Class还没有列举全,还有许多。所以过度工程化,导致其体积特别大。
    一个好的设计并不需要把每个逻辑点都抽象出来,局部过程化,整体OO是可以。如AlloyFinger的设计。仅仅只有Vector2和AlloyFinger,在touchstart、touchmove、touchend是可以trigger出相关的手势事件的,简单、直接!hammerjs能支持的手势,AlloyFinger都能支持。

    2.3 touchmove事件

    当用户在触摸屏上移动触点(手指)的时候,触发这个事件。一定是先要触发touchstart事件,再有可能触发 touchmove 事件。

    ​touchmove 事件的target 与最先触发的 touchstart 的 target 保持一致。touchmove事件和鼠标的mousemove事件一样都会多次重复调用,所以,事件处理时不能有太多耗时操作。不同的设备,移动同样的距离 touchmove 事件的触发频率是不同的。

    注意:

    1. 即使手指移出了 原来的target 元素,则 touchmove 仍然会被一直触发,而且 target 仍然是原来的 target 元素。
    2. touchmove事件会多次重复触发,由于移动端计算资源宝贵,尽量保证事件节流
    <div>
        <p></p>
    </div>
    <script>
        var i = 1;
        var box = document.querySelector("div");
        var p = document.querySelector("p");
        box.addEventListener("touchmove", function (e){
            p.innerHTML = e.target.tagName + ", " + i++;
        })
    </script>
    

    威尼斯登录首页 14

    实战

    好了,我们的手势系统到这里就完成了。接下来要在实战中检验这套系统是否可靠,做一个简单的图片浏览器,支持图片缩放,旋转,移动,长按。

    首先,做好DOM规划,和“之前”一样,我们的事件监听机制并不直接作用在图片上,而是作用在图片的父元素上。

    威尼斯登录首页 15

    然后,可以开始使用上面的手势检测系统了。

    render() {
    
        return (
    
          <Gestures onPinch={this.onPinch} onMove={this.onMove} onRotate={this.onRotate} onDoubleTap={this.onDoubleTap} onLongPress={this.onLongPress}>
    
            <div className="wrapper" >
    
              ![](http://upload-images.jianshu.io/upload_images/2362670-f8b44d4b9101e8d6.jpg?imageMogr2/auto-orient/strip%7CimageView2/2/w/1240)
    
            </div>
    
          </Gestures>
    
        );
    
      }
    

    由于我们的手势系统检测的增量,因此不能直接把增量应用在对象上,而是需要把这些增量累加。以旋转为例:

    onRotate(event) {
    
        //对增量进行累加
    
        this.angle += event.angle
    
        this.setState({
    
          angle:this.angle
    
        });
    
      }
    

    至此,我们的手势检测就完成了。

    源码:

    在线Demo: 

    具体实现

    众所周知,浏览器暴露了四个事件给开发者,touchstart touchmove touchend touchcancel,在这四个事件的回调函数可以拿到TouchEvent。
    TouchEvent:
    touches:当前位于屏幕上的所有手指动作的列表
    targetTouches:位于当前 DOM 元素上的手指动作的列表
    changedTouches:涉及当前事件的手指动作的列表
    TouchEvent里可以拿到各个手指的坐标,那么可编程性就这么产生了。

    2.4 touchend事件

    ​ 当用户的手指抬起的时候,会触发 touchend 事件。如何用户的手指从触屏设备的边缘移出了触屏设备,也会触发 touchend 事件。

    touchend 事件的 target 也是与 touchstart 的 target 一致,即使已经移出了元素。

    威尼斯登录首页 16

    一次完整的touch事件的触发顺序和过程

    Tap点按

    威尼斯登录首页 17

    移动端click有300毫秒延时,tap的本质其实就是touchend。但是要判断touchstart的手的坐标和touchend时候手的坐标x、y方向偏移要小于30。小于30才会去触发tap。

    2.5 touchcancel事件

    ​ 当触点由于某些原因被中断时触发。有几种可能的原因如下(具体的原因根据不同的设备和浏览器有所不同):

    • 由于某个事件取消了触摸:例如触摸过程被一个模态的弹出框打断。
    • 触点离开了文档窗口,而进入了浏览器的界面元素、插件或者其他外部内容区域。
    • 当用户产生的触点个数超过了设备支持的个数,从而导致 TouchList 中最早的 Touch对象被取消

    touchcancel 事件一般用于保存现场数据。比如:正在玩游戏,如果发生了 。touchcancel 事件,则应该把游戏当前状态相关的一些数据保存起来。

    longTap长按

    威尼斯登录首页 18

    touchstart开启一个750毫秒的settimeout,如果750ms内有touchmove或者touchend都会清除掉该定时器。超过750ms没有touchmove或者touchend就会触发longTap

    3. 触摸事件对象

    TouchEvent 是一类描述手指在触摸平面(触摸屏、触摸板等)的状态变化的事件。这类事件用于描述一个或多个触点,使开发者可以检测触点的移动,触点的增加和减少,等等。

    每 个 Touch 对象代表一个触点; 每个触点都由其位置,大小,形状,压力大小,和目标 element 描述。 TouchList 对象代表多个触点的一个列表.

    swipe划

    威尼斯登录首页 19

    这里需要注意,当touchstart的手的坐标和touchend时候手的坐标x、y方向偏移要大于30,判断swipe,小于30会判断tap。那么用户到底是从上到下,还是从下到上,或者从左到右、从右到左滑动呢?可以根据上面三个判断得出,具体的代码如下:

    _swipeDirection: function (x1, x2, y1, y2) {
            return Math.abs(x1 - x2) >= Math.abs(y1 - y2) ? (x1 - x2 > 0 ? 'Left' : 'Right') : (y1 - y2 > 0 ? 'Up' : 'Down')
    }
    

    3.1 TouchEvent

    TouchEvent的属性继承了 UIEventEvent

    属性列表:

    1. TouchEvent.changedTouches: 一个 TouchList 对象,包含了代表所有从上一次触摸事件到此次事件过程中,状态发生了改变的触点的 Touch 对象。

    2. TouchEvent.targetTouches: 一个 TouchList 对象,是包含了如下触点的 Touch 对象:触摸起始于当前事件的目标 element 上,并且仍然没有离开触摸平面的触点。

    3. TouchEvent.touches: 一 个 TouchList 对象,包含了所有当前接触触摸平面的触点的 Touch 对象,无论它们的起始于哪个 element 上,也无论它们状态是否发生了变化。

     <style>
        .box {
          width: 100px;
          height: 100px;
          border: 1px solid #09c;
          background-color: #0dc;
        }
      </style>
      <div class="box"></div>
      <script>
        window.onload = function() {
          var box = document.querySelector('.box');
          box.addEventListener('touchstart', function(e) {
            console.dir(e); // 查看TouchEvent对象的属性和方法
          });
        }
      </script>
    

    威尼斯登录首页 20

    pinch捏

    这个手势是使用频率非常高的,如图像裁剪的时候放大或者缩小图片,就需要pinch。

    威尼斯登录首页 21

    如上图所示,两点之间的距离比值求pinch的scale。这个scale会挂载在event上,让用户反馈给dom的transform或者其他元素的scale属性。

    3.2 TouchList详解

    ​ 一个TouchList代表一个触摸屏幕上所有触点的列表。

    ​ 举例来讲, 如果一个用户用三根手指接触屏幕(或者触控板), 与之相关的TouchList 对于每根手指都会生成一个 Touch对象, 共计 3 个.

    1. 只读属性:length

      返回这个TouchListTouch对的个数。(就是有几个手指接触到了屏幕)

    2. 方法:item(index)

      返回TouchList中指定索引的Touch对象。

    <div>
        <p style="font-size: 50px; color: #ffffff;"></p>
    </div>
    <script>
        var box = document.querySelector("div");
        var p = document.querySelector("p");
        box.addEventListener("touchend", function (e){
            p.innerHTML = e.changedTouches.length;  //返回Touch对象的个数
            for(var i = 0; i < e.changedTouches.length; i++){
                //遍历出来每个Touch对象
                console.log(e.changedTouches.item(i));
            }
        })
    </script>
    

    威尼斯登录首页 22

    测试多个手机触摸屏幕:

    <div></div>
    <p></p>
    <script>
        var div = document.querySelector("div");
        var p = document.querySelector("p");
        div.addEventListener("touchstart", function (e){
            var msg = "touches.length: " + e.touches.length +
                    "<br> targetTouches.length: " + e.targetTouches.length +
                    "<br> changedTouches.length: " + e.changedTouches.length;
            p.innerHTML = msg;
        })
    </script>
    

    操作:

    1. 放1个手指在div上
    ![](https://upload-images.jianshu.io/upload_images/4393631-d827d0113726bdc5.jpg)
    
    1. 先放1个手指在其他地方,然后再放1个手指在div

      威尼斯登录首页 23

    2. 先放1个手指在其他地方,然后再逐渐放2个手指在div

      威尼斯登录首页 24

    rotate旋转

    威尼斯登录首页 25

    如上图所示,利用内积,可以求出两次手势状态之间的夹角θ。但是这里怎么求旋转方向呢?那么就要使用差乘(Vector Cross)。
    利用cross结果的正负来判断旋转的方向。

    威尼斯登录首页 26

    cross本质其实是面积,可以看下面的推导:

    威尼斯登录首页 27

    所以,物理引擎里经常用cross来计算转动惯量,因为力矩其实要是力乘矩相当于面积:

    威尼斯登录首页 28

    3.3 Touch详解

    Touch表示用户和触摸设备之间接触时单独的交互点(a single point of contact)。​ 这个交互点通常是一个手指或者触摸笔,​ 触摸设备通常是触摸屏或者触摸板。

    基本属性列表(都是只读):

    编号 属性名 属性说明
    1. identifier 表示每 1 个 Touch 对象 的独一无二的 identifier。有了这个 identifier 可以确保你总能追踪到这个 Touch对象。
    2. screenX 触摸点相对于屏幕左边缘的 x 坐标。
    3. screenY 触摸点相对于屏幕上边缘的 y 坐标。
    4. clientX 触摸点相对于浏览器的 viewport左边缘的 x 坐标。不会包括左边的滚动距离。
    5. clientY 触摸点相对于浏览器的 viewport上边缘的 y 坐标。不会包括上边的滚动距离。
    6. pageX 触摸点相对于 document的左边缘的 x 坐标。 与 clientX 不同的是,他包括左边滚动的距离,如果有的话。
    7. pageY 触摸点相对于 document的左边缘的 y 坐标。 与 clientY 不同的是,他包括上边滚动的距离,如果有的话。
    8. target 总是表示 手指最开始放在触摸设备上的触发点所在位置的 element。 即使已经移出了元素甚至移出了document, 他表示的element仍然不变

    案例:

    var box = document.querySelector("div");
    var p = document.querySelector("p");
    box.ontouchstart = function (e){
        var touchList = e.changedTouches;
        for (var i = 0; i < touchList.length; i++){
            var touch = touchList[i];
            var msg = `id : ${touch.identifier} <br>
                           screenX : ${touch.screenX} <br>
                           screenY : ${touch.screenY} <br>
                           clientX : ${touch.clientX} <br>
                           clientY : ${touch.clientY} <br>
                           pageX : ${touch.pageX} <br>
                           pageY : ${touch.pageY} <br>
                           target: ${touch.target.nodeName} <br>
                            `;
            p.innerHTML = msg;
        }
    }
    

    没有左右滚动:

    威尼斯登录首页 29

    左右滚动:pageX 明显大于 clientX

    威尼斯登录首页 30

    总结

    主要的一些事件触发原理已经在上面讲解,还有如multipointStart、doubleTap、singleTap、multipointEnd可以看源码,不到200行的代码应该很容易消化。trigger手势事件的同时,touchStart、touchMove、touchEnd和touchCancel同样也可以监听。
    详细的Vector2和AlloyFinger代码可以去Github上查阅:

    任何意见或者建议欢迎提issue:

    4. 封装移动端tap事件

    由于点击事件经常使用,如果用click会有延迟问题,一般我们会用touch事件模拟移动端的点击事件, 以下是封装的几个事件,仅供参考。

    (function (window){  //传入window,提高变量的查找效率
        function myQuery(selector){  //这个函数就是对外提供的接口。
            //调用这个函数的原型对象上的_init方法,并返回
            return myQuery.prototype._init(selector);
        }
        myQuery.prototype = {
            /*初始化方法,获取当前query对象的方法*/
            _init: function (selector){
                if (typeof selector == "string"){
                    //把查找到的元素存入到这个原型对象上。
                    this.ele = window.document.querySelector(selector);
                    //返回值其实就是原型对象。
                    return this;
                }
            },
            /*单击事件:
             * 为了规避click的300ms的延迟,自定义一个单击事件
             * 触发时间:
             *   当抬起手指的时候触发
             *   需要判断手指落下和手指抬起的事件间隔,如果小于500ms表示单击时间。
             *   如果是大于等于500ms,算是长按时间
             * */
            tap: function (handler){
                this.ele.addEventListener("touchstart", touchFn);
                this.ele.addEventListener("touchend", touchFn);
    
                var startTime,
                    endTime;
    
                function touchFn(e){
                    e.preventDefault()
                    switch (e.type){
                        case "touchstart":
                            startTime = new Date().getTime();
                            break;
                        case "touchend":
                            endTime = new Date().getTime();
                            if (endTime - startTime < 500){
                                handler.call(this, e);
                            }
                            break;
                    }
                }
            },
            /**
             * 长按
             * @param handler
             */
            longTag: function (handler){
                this.ele.addEventListener("touchstart", touchFn);
                this.ele.addEventListener("touchmove", touchFn);
                this.ele.addEventListener("touchend", touchFn);
                var timerId;
    
                function touchFn(e){
                    switch (e.type){
                        case "touchstart" :  //500ms之后执行
                            timerId = setTimeout(function (){
                                handler.call(this, e);
                            }, 500)
                            break;
                        case "touchmove" :
                            //如果中间有移动也清除定时器
                            clearTimeout(timerId)
                            break;
                        case "touchend" :
                            //如果在500ms之内抬起了手指,则需要定时器
                            clearTimeout(timerId);
                            break;
                    }
                }
            },
            /**
             * 左侧滑动。
             * 记录手指按下的左边,在离开的时候计算 deltaX是否满足左滑的条件         
             */
            slideLeft: function (handler){
                this.ele.addEventListener("touchstart", touchFn);
                this.ele.addEventListener("touchend", touchFn);
                var startX, startY, endX, endY;
    
                function touchFn(e){
                    e.preventDefault();
                    var firstTouch = e.changedTouches[0];
                    switch (e.type){
                        case "touchstart":
                            startX = firstTouch.pageX;
                            startY = firstTouch.pageY;
                            break;
                        case "touchend":
                            endX = firstTouch.pageX;
                            endY = firstTouch.pageY;
                            //x方向移动大于y方向的移动,并且x方向的移动大于25个像素,表示在向左侧滑动
                            if (Math.abs(endX - startX) >= Math.abs(endY - startY) && startX - endX >= 25){
                                handler.call(this, e);
                            }
                            break;
                    }
                }
            },
            /* 右侧滑动 */
            rightLeft: function (e){
                //TODO:
            }
        }
        window.$ = window.myQuery = myQuery;
    })(window);
    
    // ========================
    // 使用:
    $("div").tap(function (e){
        console.log("单击事件")
    })
    $("div").longTag(function (){
        console.log("长按事件");
    })
    $("div").slideLeft(function (e){
        console.log(this);
        this.innerHTML = "左侧滑动了....."
    })
    

    5. 触摸手势封装相关的框架及事件

    手势相关的事件一般就是tap类(触屏)和滑动(swipe)事件两类。都是基于原生的touchstart、touchmove、touchend事件,封装成不同的手势类型自定义事件。

    5.1 tap类事件

    触碰事件,我目前还不知道它和touch的区别,一般用于代替click事件,有tap longTap singleTap doubleTap四种之分。

    1. tap: 手指碰一下屏幕会触发
    2. longTap: 手指长按屏幕会触发
    3. singleTap: 手指碰一下屏幕会触发
    4. doubleTap: 手指双击屏幕会触发

    5.2 swipe类事件

    滑动事件,有swipe swipeLeft swipeRight swipeUp swipeDown 五种之分。

    1. swipe:手指在屏幕上滑动时会触发
    2. swipeLeft:手指在屏幕上向左滑动时会触发
    3. swipeRight:手指在屏幕上向右滑动时会触发
    4. swipeUp:手指在屏幕上向上滑动时会触发
    5. swipeDown:手指在屏幕上向下滑动时会触发

    威尼斯登录首页 31

    5.3 zepto的手势相关事件

    Zepto.js 是一个轻量级的针对现代高级浏览器的JavaScript库, 它适配了jQuery的大部分api,也就是jQuery怎么用,Zepto.js就怎么用。它非常小,非常适合移动端。

    Zepto.js的touch模块中封装了手势相关的代码。封装了再触摸设备上触发tap– 和 swipe– 相关事件,也适用于所有的touch(iOS, Android)和pointer事件(Windows Phone)。

    • 触屏事件:tap、singleTap、doubleTap、longTap(>750ms)
    • 滑动事件:swipe、swipeLeft,、swipeRight,、swipeUp,、swipeDown
    <style>.delete { display: none; }</style>
    
    <ul id=items>
      <li>List item 1 DELETE</li>
      <li>List item 2 DELETE</li>
    </ul>
    
    <script>
    $('#items li').swipe(function(){
      $('.delete').hide()
      $('.delete', this).show()
    })
    
    $('.delete').tap(function(){
      $(this).parent('li').remove()
    })
    </script>
    

    5.4 其他移动端手势相关库

    1. 百度云的touch.js

    2. hammer.js
      hammer提供了不仅仅tap、swipe等事件,还提供了:pan(平移)、pinch类(捏拿缩放)、 press类(按住)、 rotate类(旋转)类手势支持, hammer.js详解教程

    3. 移动端点击穿透问题


    如果某个返回按钮的位置,恰好在要返回的这个页面的带有href属性的a标签的范围内,在点击返回按钮后,页面快速切换到有a标签的页面,300ms后触发了click事件,从而触发了a标签的意外跳转,这个就是典型的点击穿透问题。罪魁祸首其实就是a标签跳转默认是click事件触发,而移动端的touch事件触发之后,依然会在300ms后触发click事件。

    解决办法:
    1.就是阻止触发touch事件完成后的click事件。
    2.不要混用touch和click事件。显然不可能都绑定click事件,因为要解决300ms延迟问题(除了fastclick),那么只能都绑定touch事件,这样click事件永远不会被触发。

    注意:zepto并没有阻止click事件,所以使用zepto的tap事件依然会导致点击穿透问题,你需要手动添加 e.preventDefault() 来阻止click事件。


    参考文章:

    1. 移动端web开发---Touch事件详解
    2. MDN:TouchEvent
    3. 移动端前端常见的触摸相关事件touch、tap、swipe等整理

    本文由威尼斯手机平台登陆发布于前端资源,转载请注明出处:威尼斯登录首页关于移动手势检测我们这里不再赘述,作为腾讯手机QQ

    关键词: