H5多点触控原理以及对多点触控的追踪


对于PC端,我们的点击事件比较简单,因为鼠标指针只有一个。

但是对于移动端来说,存在多个接触点同时操作的情况,这种情况就是我们今天要研究的主题 – 多点触控。

接触点类Touch

移动端的任何触摸都会触发一个事件,该事件会携带一个touchList列表来表示:当前有哪些接触点正在作用于我们的屏幕。

通俗点的解释,接触点可以理解为你的手指,touchList列表就代表了当前总共有几个手指正在与屏幕接触。

而touchList列表中的每一项都是Touch类的对象,它包含了我们手指与屏幕接触的必要信息:接触点的位置、压力大小、接触面积等等。

所以,在介绍之前,我们必须要先了解这个类。它代表的是屏幕上接触的点。具体属性值如下:

// Touch类 属性说明

// 接触点的唯一标识,每个接触点的id在接触过程中都是唯一的,即每个手指都会分配一个id
`identifier` 

// 接触点的位置信息,这几个属性与pc端是一样的
`clientX:0`
`clientY:0`
`pageX:0`
`pageY:0`
`screenX:0`
`screenY:0`


// 接触点的压力值大小 0.0-1.0之间
`force:0`

// 这三个参数指示了包含接触点的最小椭圆
`radiusX:0`  //椭圆x轴长
`radiusY:0`  //椭圆y轴长
`rotationAngle:0` // 椭圆旋转角度

// 接触点接触的HTML对象
`target:DocumentHTMLDom`

读者朋友可以看出来,上面这个类,准确的定义了一个接触点的各种属性。那么,在哪才能得到这些接触点的对象呢?

聪明如你,估计已经猜到了,那就是在触摸事件中。

注意属性中的identifier哦~非常重要!

触摸事件

上面也提到了,Touch类只会存在于触摸事件里。常用的触摸事件有touchestart、touchmove、touchend、touchcacel

它们都是TouchEvent类的实例,TouchEvent类的属性包含了前面介绍的接触点Touch实例列表。我们主要关注它的三个属性即可,如下:

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

  2. TouchEvent.touches
    这是一个只读属性,是一个TouchList对象,它包含了所有当前正在接触的接触点Touch对象,无论它们的起始于哪个element上,也无论它们状态是否发生了变化。

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

完整的属性列表,可以参考MDN文档https://developer.mozilla.org/zh-CN/docs/Web/API/TouchEvent。

对于手势库来说,这几个TouchList属性已经够用了。

接触点的生命周期类

想一想我们一个手指在屏幕上滑动的过程:开始接触 -> 接触时的移动 -> 接触结束。

这实际上就是一个接触点的生命周期,我们来为这一过程定义一个数据结构吧。

/**
 * touch事件的生命周期类
 * @constructor
 * */
var TouchLife = (function () {
   var id = 0;
   return function () {
      /**
       * 生命周期的id
       * @type {number}
       * */
      this.id=id++;
 
      /**
       * 开始的touch事件
       * @type {Touch}
       * */
      this.startTouchEvent = null;
 
      /**
       * 结束的touch事件
       * @type {Touch}
       * */
      this.endTouchEvent = null;
 
      /**
       * 移动的touch事件
       * @type {Touch[]}
       * */
      this.moveTouchEventList = [];
   };
})();

生命周期的开始和结束都只需要一个Touch对象,而移动可以多次移动,需要一个TouchList来保存。

另外附加了一个生命周期的ID,用来唯一标识一次生命周期。

多点触控的追踪器

有了接触点的生命周期,我们还需要一个追踪器,用来追踪多个接触点的生命周期。

试想一下,对于PC,只有一个鼠标指针,我们能很好的记录它的运行轨迹。

但是对于移动端,当存在多个接触点同时作用于屏幕时,怎么来记录他们的运行轨迹呢?

这就是追踪器的主要功能:记录接触点的运行轨迹

除此之外,在某个生命周期状态变化的时候,追踪器应该能通知应用程序做处理。

这里直接给出它的实现:

/**
 * touch事件追踪器
 * @param opt
 * @param opt.target   被追踪的dom对象
 * @constructor
 */
function TouchLifeTracer(opt) {
   /**
    * 追踪的对象
    * */
   this.target = opt.target;
 
   /**
    * 作用于target的所有生命周期,包含存活和死亡的周期
    * */
   this._lifeList = [];
 
   /**
    * 当前存活的生命周期,正在与target接触的触摸点生命周期
    * */
   this.currentLifeList = [];
 
   /**
    * 某个生命周期开始
    * @type {function}
    * @param callback(life)
    * */
   this.onlifestart = null;
 
   /**
    * 某个生命周期状态变更
    * @type {function}
    * @param callback(life)
    * */
   this.onlifechange = null;
 
   /**
    * 某个生命周期开始
    * @type {function}
    * @param callback(life)
    * */
   this.onlifeend = null;
 
   /**
    * 添加生命周期
    * @param life {TouchLife} 生命周期
    * @return {*}
    */
   this.addLife = function (life) {
      this._lifeList.push(life);
   };
 
   /**
    * 根据identifier查找生命周期,此方法只能在生命周期内使用
    * @param identifier
    * @return {*}
    */
   this.findCurrentLifeByTouchID = function (identifier) {
      for(var i=0;i<this.currentLifeList.length;i++){
         var life = this.currentLifeList[i];
         if(life.startTouchEvent.identifier===identifier)
            return life;
      }
   };
 
   /**
    * 根据touchID删除当前触摸的生命周期
    * @param identifier
    * @return {boolean}
    */
   this.deleteCurrentLifeByTouchID = function (identifier) {
      for(var i=0;i<this.currentLifeList.length;i++){
         var life = this.currentLifeList[i];
         if(life.startTouchEvent.identifier===identifier){
            this.currentLifeList.splice(i,1);
            return true;
         }
      }
      return false;
   };
 
 
   /**
    * 初始化
    */
   this.init = function () {
      var self = this;
      this.target.addEventListener("touchstart",function (e) {
         e.preventDefault();
         var touchLife = new TouchLife();
         touchLife.startTouchEvent = e.changedTouches[0];
         self.addLife(touchLife);
         self.currentLifeList.push(touchLife);
         self.onlifestart && self.onlifestart(life);
      });
 
      this.target.addEventListener('touchmove',function (e) {
         e.preventDefault();
         var touches = e.changedTouches;
         for(var i=0;i<touches.length;i++){
            var touch = touches[i];
            var life = self.findCurrentLifeByTouchID(touch.identifier);
            life.moveTouchEventList.push(touch);
            self.onlifechange && self.onlifechange(life);
         }
      });
      this.target.addEventListener('touchend',function (e) {
         e.preventDefault();
         var touch = e.changedTouches[0];
         var life = self.findCurrentLifeByTouchID(touch.identifier);
         life.endTouchEvent = touch;
         self.deleteCurrentLifeByTouchID(touch.identifier);
         self.onlifeend && self.onlifeend(life);
      });
   };
 
   this.init();
}

上面这个算是最简单的追踪器了,它仅仅包含了最基本的属性和方法。包括:

  • 几个必要的属性:追踪器作用的对象、存活的生命周期、全部生命周期
  • 几个必要的方法:新增生命周期、删除生命周期、查找生命周期、追踪器的初始化
  • 几个必要的事件:生命周期的开始、生命周期的变化、生命周期的结束

存活的生命周期,对应于我们正在接触屏幕的手指。

而全部生命周期,对应于我们所有接触屏幕的历史记录。

有了这个追踪器,我们在监控多点触控的时候,就相当的方便了。比如:

// 获取元素
var a1 = document.getElementById('canvasID');
 
// 初始化
var tracer = new TouchLifeTracer({target:a1});
tracer.onlifestart = function (life) {
   //todo with life start
   console.log('检测到一个新的接触点触摸元素');
};
tracer.onlifeend = function (life) {
   //todo with life end
   console.log('检测到一个接触点离开元素');
};

抛砖引玉

有了这个追踪器,是不是就可以自己撸一个手势库出来了呢?答案是肯定的。

手势库的原理,也就是多点触控+手势的判断条件,仅此而已!

百度搜手势库比较靠前的,比如Hammer.js,比如AlloyFinger,这些库都是基于这两点基础知识来对手势进行模拟的。

始终相信,别人能做到的,我们也能做到!

那么,下篇文章我们就来真正的实现一个手势库,敬请期待!

觉得有用,还请点赞收藏!
励志前端,CSDN唯一账号!关注我,带你了解更多前端知识!

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

励志前端小黑哥

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值