资讯专栏INFORMATION COLUMN

原生js实现移动端点击、长按、左滑、右滑、上滑、下滑等事件模拟

WrBug / 3583人阅读

摘要:原理如下监听的和事件。代表的绝对值,左右滑动,右滑,反之左滑。代码如下用事件模拟点击左滑右滑上拉下拉等时间,是利用和两个事件发生的位置来确定是什么操作。支持六个事件是左滑事件,是右滑事件,是上滑事件,下滑事件,点击事件,长按点击事件。

github地址:https://github.com/xubaodian/...
为什么要模拟这些事件?

1、上述这些事件中,浏览器直接支持的事件只有点击,而其它事件使用频率也很高。

2、移动端web原生点击事件会有300ms的延迟,因为用户肯能双击,为了判断用户是单击还是双击,所以会有这个延迟,这个延迟会衍生很多问题,例如点击穿透。所以我们可以不用原生的点击事件,而使用模拟点击事件。

如何模拟这些事件呢?

我们可以总结这些操作,都是手指先触摸屏幕,然后在离开。不同点在于滑动事件手指有位移,而点击事件手指没有位移。

首先想到的就是所有浏览器都是支持touchstart、touchmove和touchend事件的,我们可以利用这些事件来模拟上述事件。

原理如下:

1、监听dom的touchstart和touchend事件。

2、分别记录touchstart、touchend事件的位置和时间,计算位移delta(包括x和y)和时间间隔timegap。

3、根据delta和timegap的值,判断属于哪种事件。有两种情况:

delta中x和y都很小

这是点击操作,用户点击按钮等时,理论上是不会有位移的,但是实际中也可能发生一个很小的位移,毕竟手指不是精密仪器。

如果时间间隔timegap较小,则属于点击,如果timegap较大,属于长按操作。

delta中的x或y比较大

这种情况下,就是手指发生滑动操作了,至于是左右滑动,还是上下滑动,根据x和y的大小来判断。

|x| > |y|(|x|代表x的绝对值),左右滑动,x>0,右滑,反之左滑。

|x| <= |y|,上下滑动,y>0,下滑,反之上滑。

这样就模拟了移动端web中的这几个事件了。代码如下:

/**
     * 用touch事件模拟点击、左滑、右滑、上拉、下拉等时间,
     * 是利用touchstart和touchend两个事件发生的位置来确定是什么操作。
     * 例如:
     * 1、touchstart和touchend两个事件的位置基本一致,也就是没发生位移,那么可以确定用户是想点击按钮等。
     * 2、touchend在touchstart正左侧,说明用户是向左滑动的。
     * 利用上面的原理,可以模拟移动端的各类事件。
    **/
   const EventUtil = (function() {

    //支持事件列表
    let eventArr = ["eventswipeleft", "eventswiperight", "eventslideup", "eventslidedown", "eventclick", "eventlongpress"];

    //touchstart事件,delta记录开始触摸位置
    function touchStart(event) {
      this.delta = {};
      this.delta.x = event.touches[0].pageX;
      this.delta.y = event.touches[0].pageY;
      this.delta.time = new Date().getTime();
    }

    /**
     * touchend事件,计算两个事件之间的位移量
     * 1、如果位移量很小或没有位移,看做点击事件
     * 2、如果位移量较大,x大于y,可以看做平移,x>0,向右滑,反之向左滑。
     * 3、如果位移量较大,x小于y,看做上下移动,y>0,向下滑,反之向上滑
     * 这样就模拟的移动端几个常见的时间。
     * */
    function touchEnd(event) {
      let delta = this.delta;
      delete this.delta;
      let timegap = new Date().getTime() - delta.time;
      delta.x -= event.changedTouches[0].pageX;
      delta.y -= event.changedTouches[0].pageY;  
      if (Math.abs(delta.x) < 5 && Math.abs(delta.y) < 5) {
        if (timegap < 1000) {
          if (this["eventclick"]) {
            this["eventclick"].map(function(fn){
              fn(event);
            });
          }
        } else {
          if (this["eventlongpress"]) {
            this["eventlongpress"].map(function(fn){
              fn(event);
            });
          }
        }
        return;
      }
      if (Math.abs(delta.x) > Math.abs(delta.y)) {
        if (delta.x > 0) {
          if (this["eventswipeleft"]) {
            this["eventswipeleft"].map(function(fn){
              fn(event);
            });
          }
        } else {
          this["eventswiperight"].map(function(fn){
            fn(event);
          });
        }
      } else {
        if (delta.y > 0) {
          if (this["eventslidedown"]) {
            this["eventslidedown"].map(function(fn){
              fn(event);
            });
          }
        } else {
          this["eventslideup"].map(function(fn){
            fn(event);
          });
        }
      }
    }

    function bindEvent(dom, type, callback) {
      if (!dom) {
        console.error("dom is null or undefined");
      }
      let flag  = eventArr.some(key => dom[key]);
      if (!flag) {
        dom.addEventListener("touchstart", touchStart);
        dom.addEventListener("touchend", touchEnd);
      }
      if (!dom["event" + type]) {
        dom["event" + type] = [];
      }
      dom["event" + type].push(callback);
    }

    function removeEvent(dom, type, callback) {
      if (dom["event" + type]) {
        for(let i = 0; i < dom["event" + type].length; i++) {
          if (dom["event" + type][i] === callback) {
            dom["event" + type].splice(i, 1);
            i--;
          }
        }
        if (dom["event" + type] && dom["event" + type].length === 0) {
          delete dom["event" + type];
          let flag  = eventArr.every(key => !dom[key]);
          if (flag) {
            dom.removeEventListener("touchstart", touchStart);
            dom.removeEventListener("touchend", touchEnd);
          }
        }
      }
    }
    return {
      bindEvent,
      removeEvent
    }
   })();

在闭包中定义了几个事件处理操作,EventUtil有两个方法,bindEvent绑定事件,removeEvent是移除事件绑定。

支持六个事件:

swipeleft是左滑事件,swiperight是右滑事件,slideup是上滑事件,slidedown下滑事件,click点击事件,longpress长按点击事件。

使用案例如下:

在页面中引用上述代码:

测试代码如下,代码中有注释,可以看到如何应用这些模拟事件:




  
  
  Page Title
  
  
  
  


  

测试效果如下:

有疑问的可以留言或发送邮件至472784995@qq.com。

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/101476.html

相关文章

  • [译] 在 Angular 中使用 HammerJS (触摸手势)

    摘要:是一个为应用添加触摸手势的非常受欢迎的库文中将看到结合一起使用是多么的简单原文示例是针对版本经过测试在目前最新的版本中此教程依然适用文章将以来统一代称版本名词滑动和类似但滑动更快速无粘滞左滑右滑上滑下滑头像轮播简介我们将构建一个头像轮播可以 HammerJS 是一个为 web 应用添加触摸手势的非常受欢迎的库,文中,将看到 Angular 结合 HammerJS 一起使用是多么的简单 ...

    lifesimple 评论0 收藏0
  • 如何实现swipe、tap、longTap自定义事件

    摘要:分别存储事件的定时器。事件定时器延时时间存储事件对象滑动方向判断我们根据下图以及对应的代码来理解滑动的时候方向是如何判定的。取消长按,以及取消所有事件取消长按取消所有事件方式都是类似,先调用取消定时器,然后释放对应的变量,等候垃圾回收。 前言 移动端原生支持touchstart、touchmove、touchend等事件,但是在平常业务中我们经常需要使用swipe、tap、double...

    罗志环 评论0 收藏0
  • 纯 HTML+CSS+JavaScript 编写的计算器应用

    摘要:不允许再有其他文件,不允许再有单独的文件。必须支持标准的四则运算。请在收到邮件的小时内独立完成本测试,并回复本邮件。项目地址最终版的计算器,项目地址和预览图片在。并且使用单位来进行自动计算尺寸。 一道笔试题 之前偶然看到一个公司的笔试题,题目如下: 用HTML5、CSS3、JavaScript,做一个网页,实现如下图形式计算器 showImg(https://segmentfault....

    PiscesYE 评论0 收藏0
  • 纯 HTML+CSS+JavaScript 编写的计算器应用

    摘要:不允许再有其他文件,不允许再有单独的文件。必须支持标准的四则运算。请在收到邮件的小时内独立完成本测试,并回复本邮件。项目地址最终版的计算器,项目地址和预览图片在。并且使用单位来进行自动计算尺寸。 一道笔试题 之前偶然看到一个公司的笔试题,题目如下: 用HTML5、CSS3、JavaScript,做一个网页,实现如下图形式计算器 showImg(https://segmentfault....

    youkede 评论0 收藏0

发表评论

0条评论

WrBug

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<