zepto.js中的Touch事件

  在移动端经常需要处理滑动事件,比如banner图的左滑右滑,都需要事件响应,这在PC端是没有这类事件的。而在zepto.js中,下载下来的代码也并没有自动加上对这类事件的支持,但是有提供相应的模块,需要你自己加上去(下表中最后一个touch模块):

moduledefaultdescription
zepto 核心模块;包含许多方法
event 通过on()off()处理事件
ajax XMLHttpRequest 和 JSONP 实用功能
form 序列化 & 提交web表单
ie 增加支持桌面的Internet Explorer 10+和Windows Phone 8。
detect   提供 $.os和 $.browser消息
fx   The animate()方法
fx_methods   以动画形式的 showhidetoggle, 和 fade*()方法.
assets   实验性支持从DOM中移除image元素后清理iOS的内存。
data   一个全面的 data()方法, 能够在内存中存储任意对象。
deferred   提供 $.Deferredpromises API. 依赖"callbacks" 模块. 
当包含这个模块时候, $.ajax() 支持promise接口链式的回调。
callbacks   为"deferred"模块提供 $.Callbacks
selector   实验性的支持 jQuery CSS 表达式 实用功能,比如 $('div:first')el.is(':visible')
touch   在触摸设备上触发tap– 和 swipe– 相关事件。这适用于所有的`touch`(iOS, Android)和`pointer`事件(Windows Phone)。

  这些模块的代码可以直接加在zepto.js的最底部,像拼积木一样,选择模块然后加上去拼合一份适合自己项目的加强版zepto.js。

  关于代码中是如何实现的事件这里不做过多解读,核心肯定是通过touch坐标的差值判断到底是左滑还是上滑。这里主要讲的是touch模块中的一个坑,那就是会和国内的主流手机浏览器的默认事件冲突。具体表现就是,给一个元素绑定左滑动后,在UC浏览器中滑动该元素会出现页面被切换的情况。

  简单的测试发现:UC/QQ都与touch事件发生冲突,安卓自带浏览器和谷歌浏览器不会有此现象。touch这个脚本本来就是外国人写的,当然就无视中国特色了。这里对touch做了一些简单的修改,达到避免这种冲突发生。

  首先引入zepto(已包含touch)之后,代码如下:  

$('#div').swipeLeft(function(e){
     alert(1);	  	 
})

  在PC上用谷歌开发工具模拟器看,没有任何问题。

  在手机上用UC或者QQ看,悲催了,页面直接被滑跑了。你可以试试,体验一下总比别人告诉你印象深刻。

  很明显,这里需要阻止默认事件,比如这样改一改:  

$('#div').on('touchstart',function(e){
   e.preventDefault();
}).swipeLeft(function(e){
   alert(1);	  	 
})

  手机上便可以弹出1。然后事情并没有结束。

  首先这样写太丑陋了,每个swipe之前都要去阻止默认事件,更加难以接受的是,这个时候我上下滑动该元素也会阻止默认事件,也就意味着,当页面需要正常上下滚动的时候,手指在该元素上将滑动不了!

  好吧,那我就去改touch。

  首先,找到touch事件快捷绑定的代码块:  

;['swipe', 'swipeLeft', 'swipeRight', 'swipeUp', 'swipeDown', 'doubleTap', 'tap', 'singleTap', 'longTap'].forEach(function(m){
    $.fn[m] = function(callback){  
      return this.bind(m, callback) 
    }
})

  修改如下:

;['swipe', 'swipeLeft', 'swipeRight', 'swipeUp', 'swipeDown', 'doubleTap', 'tap', 'singleTap', 'longTap'].forEach(function(m){
    $.fn[m] = function(callback){  
      if(!this.data('swipeEvents')){
        this.data('swipeEvents',m);
      }else{
        this.data('swipeEvents',this.data('swipeEvents')+'|'+m);
      }       
      return this.bind(m, callback) 
    }
})

  目的:给所有直接调用swipe的元素,默默的标识上一个数据,该数据就是本元素被绑定的swipe事件的名称,绑定多个swipe时以 | 隔开。

  第二步:找到给body绑定的事件冒泡的touchmove事件代码:

.bind('touchmove', function(e){
      cancelLongTap()
      touch.x2 = e.touches[0].pageX
      touch.y2 = e.touches[0].pageY
})

  修改为:

.bind('touchmove', function(e){
      cancelLongTap()
      touch.x2 = e.touches[0].pageX
      touch.y2 = e.touches[0].pageY
      
      if(touch.el&&touch.el.data('swipeEvents')){  
        if(Math.abs(touch.x2-touch.x1)>4&&/Left|Right/.test(touch.el.data('swipeEvents'))){
          e.preventDefault();
        }else if(Math.abs(touch.y2-touch.y1)>4&&/Up|Down/.test(touch.el.data('swipeEvents'))){
          e.preventDefault();
        }
      }      
 })

  目的:只要该元素绑定了swipe事件,并且滑动轨迹和swipe事件匹配上,则阻止默认事件。这样,匹配不上的将不被阻止,比如你只是绑定的左右滑动,当touch是上下滑动的时候,不会执行到preventDefault。

  经过UC和QQ测试都没有问题。

  但是,事情还是没有结束。因为,你只要换个方式绑定swipe就挂了,比如这样子:  

$('#div').on('swipeLeft',function(e){

  因为我们只给$.fn[swipe]的时候做了处理,也就是元素直接调用siwpe方法时,才会有效,一旦调用on方法就嗝屁了。当然,一般元素直接调用swipe就可以了,但是如果想用到事件委托,给大量元素批量绑定的时候就必须用到on啊,这怎么办?不好意思,我这里没有给出好办法,因为on这个方法不好改也不能改,改touch已经是心惊胆战了,直接改zepto水平不够。所以暂时还没想到最好的办法,谁知道完美解决办法的请留言。

   

原文地址:https://www.cnblogs.com/webLilingyun/p/5542283.html