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 相关文章推荐
今天是星期几的4种JS代码写法
Sep 17 Javascript
jquery 字符串切割函数substring的用法说明
Feb 11 Javascript
js中跨域方法原理详解
Jul 19 Javascript
javascript实现简单计算器效果【推荐】
Apr 19 Javascript
PHP+jquery+ajax实现分页
Dec 09 Javascript
jQuery控制元素隐藏和显示
Mar 03 Javascript
jQuery实现上传图片前预览效果功能
Aug 03 jQuery
记录一篇关于redux-saga的基本使用过程
Aug 18 Javascript
对vuejs的v-for遍历、v-bind动态改变值、v-if进行判断的实例讲解
Aug 27 Javascript
详解webpack之图片引入-增强的file-loader:url-loader
Oct 08 Javascript
Layui给switch添加响应事件的例子
Sep 03 Javascript
javascript浅层克隆、深度克隆对比及实例解析
Feb 09 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浮点数精度问题汇总
2015/05/13 PHP
PHP simplexml_load_file()函数讲解
2019/02/03 PHP
thinkphp5框架实现的自定义扩展类操作示例
2019/05/16 PHP
Laravel 解决419错误 -ajax请求错误的问题(CSRF验证)
2019/10/25 PHP
json的前台操作和后台操作实现代码
2012/01/20 Javascript
jquery修改网页背景颜色通过css方法实现
2014/06/06 Javascript
AngularJS控制器controller给模型数据赋初始值的方法
2017/01/04 Javascript
理解nodejs的stream和pipe机制的原理和实现
2017/08/12 NodeJs
JS沙箱模式实例分析
2017/09/04 Javascript
AngularJS实现注册表单验证功能
2017/10/16 Javascript
微信小程序版翻牌小游戏
2018/01/26 Javascript
Angular中使用better-scroll插件的方法
2018/03/27 Javascript
解决layer弹层遮罩挡住窗体的问题
2018/08/17 Javascript
JavaScript创建对象方式总结【工厂模式、构造函数模式、原型模式等】
2018/12/19 Javascript
JavaScript实现打砖块游戏
2020/02/25 Javascript
如何HttpServletRequest文件对象并储存
2020/08/14 Javascript
Python采用raw_input读取输入值的方法
2014/08/18 Python
朴素贝叶斯算法的python实现方法
2014/11/18 Python
在Mac OS上搭建Python的开发环境
2015/12/24 Python
pycharm 配置远程解释器的方法
2018/10/28 Python
值得收藏的10道python 面试题
2019/04/15 Python
Python自动创建Excel并获取内容
2020/09/16 Python
Python实现简单的2048小游戏
2021/03/01 Python
html+js 实现markdown编辑器效果
2019/10/23 HTML / CSS
世界上最大的罕见唱片、CD和音乐纪念品网上商店:991.com
2018/05/03 全球购物
建材业务员岗位职责
2013/12/08 职场文书
技术副厂长岗位职责
2013/12/26 职场文书
九年级家长会邀请函
2014/01/15 职场文书
节约电力资源的建议书
2014/03/12 职场文书
安全生产管理责任书
2014/04/16 职场文书
北京申奥口号
2014/06/19 职场文书
精神文明建设先进个人事迹材料
2014/12/24 职场文书
大学毕业生自我评价
2015/03/02 职场文书
2015年学校教科室工作总结
2015/07/20 职场文书
2015年学校消防安全工作总结
2015/10/14 职场文书
爱岗敬业先进典型事迹材料(2016推荐版)
2016/02/26 职场文书