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 相关文章推荐
你所要知道JS(DHTML)中的一些技巧
Jan 09 Javascript
js关闭子窗体刷新父窗体实现方法
Dec 04 Javascript
js取float型小数点后两位数的方法
Jan 18 Javascript
使用node.js半年来总结的 10 条经验
Aug 18 Javascript
node.js中的fs.fstatSync方法使用说明
Dec 15 Javascript
浅析JS中常用类型转换及运算符表达式
Jul 23 Javascript
js前端导出Excel的方法
Nov 01 Javascript
Express本地测试HTTPS的示例代码
Jun 06 Javascript
vue 项目中使用Loading组件的示例代码
Aug 31 Javascript
Javascript读取上传文件内容/类型/字节数
Apr 30 Javascript
vue设置默认首页的操作
Aug 12 Javascript
vue如何清除浏览器历史栈
May 25 Vue.js
删除条目时弹出的确认对话框
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 程序员的调试技术小结
2009/11/15 PHP
关于php内存不够用的快速解决方法
2013/10/26 PHP
php setcookie函数的参数说明及其用法
2014/04/20 PHP
Laravel 5 框架入门(三)
2015/04/09 PHP
php实现用于计算执行时间的类实例
2015/04/18 PHP
PHP实现简单爬虫的方法
2015/07/29 PHP
php 伪造HTTP_REFERER页面URL来源的三种方法
2016/09/22 PHP
Jquery ajax不能解析json对象,报Invalid JSON错误的原因和解决方法
2010/03/27 Javascript
到处都是jQuery选择器的年代 不了解它们的性能,行吗
2012/06/18 Javascript
jQuery function的正确书写方法
2013/08/02 Javascript
js 判断控件获得焦点的示例代码
2014/03/04 Javascript
node.js Web应用框架Express入门指南
2014/05/28 Javascript
JavaScript控制table某列不显示的方法
2015/03/16 Javascript
JS获取Table中td值的方法
2015/03/19 Javascript
javascript图片滑动效果实现
2021/01/28 Javascript
jQuery Timelinr实现垂直水平时间轴插件(附源码下载)
2016/02/16 Javascript
微信小程序实现折叠展开效果
2018/07/19 Javascript
微信小程序swiper使用网络图片不显示问题解决
2019/12/13 Javascript
简单了解JavaScript弹窗实现代码
2020/05/07 Javascript
vue cli3.0打包上线静态资源找不到路径的解决操作
2020/08/03 Javascript
k8s node节点重新加入master集群的实现
2021/02/22 Javascript
[01:05:00]2018国际邀请赛 表演赛 Pain vs OpenAI
2018/08/24 DOTA
windows下Python实现将pdf文件转化为png格式图片的方法
2017/07/21 Python
浅谈numpy生成数组的零值问题
2018/11/12 Python
python中实现控制小数点位数的方法
2019/01/24 Python
浅析python 字典嵌套
2020/09/29 Python
CSS3移动端vw+rem不依赖JS实现响应式布局的方法
2019/01/23 HTML / CSS
Ruby如何创建一个线程
2013/03/10 面试题
医科大学生的自我评价
2013/12/04 职场文书
秦兵马俑教学反思
2014/02/07 职场文书
运动会闭幕式解说词
2014/02/21 职场文书
党员2014两会学习心得体会
2014/03/17 职场文书
影视广告专业求职信
2014/09/02 职场文书
小学生纪念九一八事变演讲稿
2014/09/14 职场文书
Redis实现订单自动过期功能的示例代码
2021/05/08 Redis
Go语言空白表示符_的实例用法
2021/07/04 Golang