javascript移动设备Web开发中对touch事件的封装实例


Posted in Javascript onJune 05, 2014

在触屏设备上,一些比较基础的手势都需要通过对 touch 事件进行二次封装才能实现。
zepto 是移动端上使用率比较高的一个类库,但是其 touch 模块模拟出来的一些事件存在一些兼容性问题,如 tap 事件在某些安卓设备上存在事件穿透的 bug,其他类型的事件也或多或少的存在一些兼容性问题。

于是乎,干脆自己动手对这些常用的手势事件进行了封装,由于没有太多真实的设备来进行测试,可能存在一些兼容性问题,下面的代码也只是在 iOS 7、Andorid 4 上的一些比较常见的浏览器中测试通过。

tap事件

tap 事件相当于 pc 浏览器中的 click 效果,虽然在触屏设备上 click 事件仍然可用,但是在很多设备上,click 会存在一些延迟,如果想要快速响应的 “click” 事件,需要借助 touch 事件来实现。

var startTx, startTy;
element.addEventListener( 'touchstart', function( e ){

  var touches = e.touches[0];
  startTx = touches.clientX;

  startTy = touches.clientY;

}, false );
element.addEventListener( 'touchend', function( e ){

  var touches = e.changedTouches[0],

    endTx = touches.clientX,

    endTy = touches.clientY;
  // 在部分设备上 touch 事件比较灵敏,导致按下和松开手指时的事件坐标会出现一点点变化

  if( Math.abs(startTx - endTx) < 6 && Math.abs(startTy - endTy) < 6 ){

    console.log( 'fire tap event' );

  }

}, false );

doubleTap事件

doubleTap 事件是当手指在相同位置范围内和极短的时间内两次敲击屏幕时触发的事件。在部分浏览器下,doubleTap 事件会选中文本,如果不希望选中文本,可以给元素添加 user-select:none 的 css 属性。

var isTouchEnd = false,

  lastTime = 0,

  lastTx = null,

  lastTy = null,

  firstTouchEnd = true,

  body = document.body,

  dTapTimer, startTx, startTy, startTime;
element.addEventListener( 'touchstart', function( e ){

  if( dTapTimer ){

    clearTimeout( dTapTimer );

    dTapTimer = null;

  }
  var touches = e.touches[0];
  startTx = touches.clientX;

  startTy = touches.clientY;   

}, false );
element.addEventListener( 'touchend', function( e ){

  var touches = e.changedTouches[0],

    endTx = touches.clientX,

    endTy = touches.clientY,

    now = Date.now(),

    duration = now - lastTime;
  // 首先要确保能触发单次的 tap 事件

  if( Math.abs(startTx - endTx) < 6 && Math.abs(startTx - endTx) < 6 ){

    // 两次 tap 的间隔确保在 500 毫秒以内

    if( duration < 301 ){

      // 本次的 tap 位置和上一次的 tap 的位置允许一定范围内的误差

      if( lastTx !== null &&

        Math.abs(lastTx - endTx) < 45 &&

        Math.abs(lastTy - endTy) < 45 ){
        firstTouchEnd = true;

        lastTx = lastTy = null;

        console.log( 'fire double tap event' );

      }

    }

    else{

      lastTx = endTx;

      lastTy = endTy;

    }

  }

  else{

    firstTouchEnd = true;

    lastTx = lastTy = null;

  }
  lastTime = now;

}, false );
// 在 iOS 的 safari 上手指敲击屏幕的速度过快,

// 有一定的几率会导致第二次不会响应 touchstart 和 touchend 事件

// 同时手指长时间的touch不会触发click
if( ~navigator.userAgent.toLowerCase().indexOf('iphone os') ){
  body.addEventListener( 'touchstart', function( e ){

      startTime = Date.now();

  }, true );
  body.addEventListener( 'touchend', function( e ){

      var noLongTap = Date.now() - startTime < 501;
      if( firstTouchEnd ){

          firstTouchEnd = false;

          if( noLongTap && e.target === element ){

              dTapTimer = setTimeout(function(){

                  firstTouchEnd = true;

                  lastTx = lastTy = null;

                  console.log( 'fire double tap event' );

              }, 400 );

          }

      }

      else{

          firstTouchEnd = true;

      }

  }, true );
// iOS 上手指多次敲击屏幕时的速度过快不会触发 click 事件

element.addEventListener( 'click', function( e ){

  if( dTapTimer ){

    clearTimeout( dTapTimer );

    dTapTimer = null;

    firstTouchEnd = true;

  }

}, false );
}

longTap事件

longTap 事件是当手指长时间按住屏幕保持不动时触发的事件。

var startTx, startTy, lTapTimer;
element.addEventListener( 'touchstart', function( e ){

  if( lTapTimer ){

    clearTimeout( lTapTimer );

    lTapTimer = null;

  }
  var touches = e.touches[0];
  startTx = touches.clientX;

  startTy = touches.clientY;
  lTapTimer = setTimeout(function(){

    console.log( 'fire long tap event' );

  }, 1000 );
  e.preventDefault();

}, false );
element.addEventListener( 'touchmove', function( e ){

  var touches = e.touches[0],

    endTx = touches.clientX,

    endTy = touches.clientY;
  if( lTapTimer && (Math.abs(endTx - startTx) > 5 || Math.abs(endTy - startTy) > 5) ){

    clearTimeout( lTapTimer );

    lTapTimer = null;

  }

}, false );
element.addEventListener( 'touchend', function( e ){

  if( lTapTimer ){

    clearTimeout( lTapTimer );

    lTapTimer = null;

  }

}, false );

swipe事件

swipe 事件是当手指在屏幕上滑动后触发的事件,根据手指滑动的方向又分为 swipeLeft (向左)、swipeRight (向右)、swipeUp (向上)、swipeDown (向下)。

var isTouchMove, startTx, startTy;
element.addEventListener( 'touchstart', function( e ){

  var touches = e.touches[0];
  startTx = touches.clientX;

  startTy = touches.clientY;

  isTouchMove = false;

}, false );
element.addEventListener( 'touchmove', function( e ){

  isTouchMove = true;

  e.preventDefault();

}, false );
element.addEventListener( 'touchend', function( e ){

  if( !isTouchMove ){

    return;

  }
  var touches = e.changedTouches[0],

    endTx = touches.clientX,

    endTy = touches.clientY,

    distanceX = startTx - endTx

    distanceY = startTy - endTy,

    isSwipe = false;
  if( Math.abs(distanceX) >= Math.abs(distanceY) ){

    if( distanceX > 20 ){

      console.log( 'fire swipe left event' );

      isSwipe = true;

    }

    else if( distanceX < -20 ){

      console.log( 'fire swipe right event' );    

      isSwipe = true;

    }

  }

  else{

    if( distanceY > 20 ){

      console.log( 'fire swipe up event' );        

      isSwipe = true;

    }

    else if( distanceY < -20 ){

      console.log( 'fire swipe down event' );         

      isSwipe = true;

    }

  }
  if( isSwipe ){

    console.log( 'fire swipe event' );

  }

}, false );

上面模拟的事件都封装在 MonoEvent 中了。完整代码地址:https://github.com/chenmnkken/monoevent,需要的朋友看看吧~

 PS:这里再为大家推荐一款关于JS事件的在线查询工具,归纳总结了JS常用的事件类型与函数功能:

javascript事件与功能说明大全:

Javascript 相关文章推荐
浅谈关于JavaScript的语言特性分析
Apr 11 Javascript
jQuery bxCarousel实现图片滚动切换效果示例代码
May 15 Javascript
Jquery 跨域访问 Lightswitch OData Service的方法
Sep 11 Javascript
jquery计算鼠标和指定元素之间距离的方法
Jun 26 Javascript
Uploadify上传文件方法
Mar 16 Javascript
Bootstrap popover用法详解
Dec 22 Javascript
利用Vue.js+Node.js+MongoDB实现一个博客系统(附源码)
Apr 24 Javascript
详解webpack4升级指南以及从webpack3.x迁移
Jun 12 Javascript
vue.js与后台数据交互的实例讲解
Aug 08 Javascript
Node.js 的 GC 机制详解
Jun 03 Javascript
在Vue环境下利用worker运行interval计时器的步骤
Aug 01 Javascript
详解vue beforeEach 死循环问题解决方法
Feb 25 Javascript
删除条目时弹出的确认对话框
Jun 05 #Javascript
判断复选框是否被选中的两种方法
Jun 04 #Javascript
jQuery页面加载初始化常用的三种方法
Jun 04 #Javascript
JS替换字符串中字符即替换全部而不是第一个
Jun 04 #Javascript
ActiveX控件与Javascript之间的交互示例
Jun 04 #Javascript
使用jquery修改表单的提交地址基本思路
Jun 04 #Javascript
jQuery操作元素css样式的三种方法
Jun 04 #Javascript
You might like
转换中文日期的PHP程序
2006/10/09 PHP
调整优化您的LAMP应用程序的5种简单方法
2011/06/26 PHP
支持中文和其他编码的php截取字符串函数分享(截取中文字符串)
2014/03/13 PHP
PHP实现二维数组根据key进行排序的方法
2016/12/30 PHP
Yii框架连表查询操作示例
2019/09/06 PHP
IE autocomplete internet explorer's autocomplete
2007/06/30 Javascript
jquery 框架使用教程 AJAX篇
2009/10/11 Javascript
Jquery 改变radio/checkbox选中状态,获取选中的值(示例代码)
2013/12/12 Javascript
如何用javascript计算文本框还能输入多少个字符
2015/07/29 Javascript
jquery实现简单的表单验证
2015/11/17 Javascript
AngularJS表单验证中级篇(3)
2016/09/28 Javascript
JS实现iframe自适应高度的方法示例
2017/01/07 Javascript
Bootstrap笔记之缩略图、警告框实例详解
2017/03/09 Javascript
微信小程序教程系列之设置标题栏和导航栏(7)
2020/06/29 Javascript
js中less常用的方法小结
2017/08/09 Javascript
写给小白看的JavaScript异步
2017/11/29 Javascript
详解React 服务端渲染方案完美的解决方案
2018/12/14 Javascript
微信小程序与公众号实现数据互通的方法
2019/07/25 Javascript
Vuex modules模式下mapState/mapMutations的操作实例
2019/10/17 Javascript
JavaScript实现Excel表格效果
2020/02/07 Javascript
springboot+vue实现文件上传下载
2020/11/17 Vue.js
python实现图片处理和特征提取详解
2017/11/13 Python
使用EduBlock轻松学习Python编程
2018/10/08 Python
django-rest-swagger对API接口注释的方法
2019/08/29 Python
Python计算两个矩形重合面积代码实例
2019/09/16 Python
PyQt5如何将.ui文件转换为.py文件的实例代码
2020/05/26 Python
python实现计算器简易版
2020/12/17 Python
html5的新玩法——语音搜索
2013/01/03 HTML / CSS
中学生班主任评语
2014/01/30 职场文书
给学校的建议书
2014/03/12 职场文书
药学职务聘任书
2014/03/29 职场文书
拾金不昧锦旗标语
2014/06/27 职场文书
法人单位适用的授权委托书
2014/09/19 职场文书
企业党支部工作总结2015
2015/05/21 职场文书
mysql死锁和分库分表问题详解
2021/04/16 MySQL
Linux、ubuntu系统下查看显卡型号、显卡信息详解
2022/04/07 Servers