资讯专栏INFORMATION COLUMN

移动端VUE点击、滑动和长按等事件处理(自定义指令)

niuxiaowei111 / 2481人阅读

摘要:问题移动设备上的触摸事件如何利用它们三个来处理点击长按滑动等操作,以及如何在测试用例中模拟它们的操作参考了实现方法上这位大哥的思路移动设备的点击优化参考了感谢解决使用自定义指令来干这件事来记录开始点击的位置和时间,并在这里边判断长按操作来确

问题:

移动设备上的触摸事件:touchstart、touchmove、touchend
如何利用它们三个来处理点击、长按、滑动等操作,以及如何在测试用例中模拟它们的操作

参考了:

实现方法上 https://blog.csdn.net/qq_1775... 这位大哥的思路

移动设备的点击优化参考了MUI

//感谢!!!
解决

使用vue自定义指令来干这件事

touchstart来记录开始点击的位置和时间,并在这里边判断 长按 操作

touchend来确定结束点击的位置和时间,来区分 点击 操作以及 不同方向的滑动 操作

需要有v-on一样的修饰符来处理不同的触发条件(.stop .prevent .self .once)

移动设备上人类手指太粗导致的点击位置的问题的修正(使用Touch事件中的changeTouches来确定点击的中心位置)

移动设备滑动方向的判断(思路来自mui源码)

然后直接上操作类

class VueTouch {
  /**
   * @param el 绑定的DOM
   * @param binding 自定义指令中的binding对象
   * @param type 绑定的事件类型
   */
  constructor(el, binding, type) {
    
    let g = this;
    
    g.obj = el;
    g.binding = binding;
    g.touchType = type;
    
    g.firstTouchPosition = {x: 0, y: 0};
    g.firstTouchTime = 0;
    
   /**
     * =========================================
     * 事件绑定有两种方式
     * @example
     *  1. v-tap="showDialog" 绑定一个方法对象
     *  2. v-tap="{fn:click123, param1:1, param2:2, param3:{aaa:"123"} ...}"
     *      绑定一个JSON字面量,fn是执行的方法,后边的是需要传递的参数
     * 事件回调参数
     * @param 第一个参数是event,事件对象
     * @param 第二个参数是 binding.value,也就是v-tap=""双引号中的部分(如示例2,第二个参数就是 {fn:click123, param1:1, param2:2, param3:{aaa:"123"} ...})
     * =========================================
     */   
    g.callBack = typeof(binding.value) === "object" ? binding.value.fn : binding.value;
    
    // 事件监听回调集合
    let _listener = Object.create(null);
    
    // 事件方法
    let _listen = (type) => {
      return function (e) {
      
        /**
         * 取出修饰符(和v-on的一样)
         */
        let {stop, prevent, self, once} = g.binding.modifiers;
        
        // 配置判断
        if (stop) e.stopPropagation();
        if (prevent) e.preventDefault();
        if (once) g.obj.removeEventListener("touch" + type, _listener[type]);
        if (self && e.target !== e.currentTarget) return;
        
        g[type](e);
      }
    };
    
    _listener.start = _listen("start");
    _listener.end = _listen("end");
    _listener.move = _listen("move");
    
    // 事件绑定
    this.obj.addEventListener("touchstart", _listener.start, false);
    this.obj.addEventListener("touchend", _listener.end, false);
    this.obj.addEventListener("touchmove", _listener.move, false);
  }
  
  // 点击开始
  start(e) {
    
    let g = this;
    
    // @update 2018.3.26 这里做了一个修改
    g.moved = false; //是否移动了
    g.leaved = false; //是否离开了 
    g.longTouched = false; //是否执行了长按操作
    
    // 获取开始点击位置和时间
    g.firstTouchPosition = g.getMultiCenter(e.changedTouches);
    g.firstTouchTime = e.timeStamp;
    
    // 判断长按操作 @TODO 稍微有点触发时机上的问题,待修正
    g.time = setTimeout(function () {
      if (!g.leaved && !g.moved) {
        g.touchType === "longtap" && g.callBack(e, g.binding.value);
        g.longTouched = true;
      }
    }.bind(g), 1000);
  }
  
  // 点击结束
  end(e) {
    
    let g = this;
    
    // 点击结束获取点击位置并计算位移和时差
    let {x, y} = g.getMultiCenter(e.changedTouches);
    let _disX = x - g.firstTouchPosition.x;
    let _disY = y - g.firstTouchPosition.y;
    let _dis = Math.sqrt(_disX * _disX + _disY * _disY);
    let _timeDis = e.timeStamp - g.firstTouchTime;
    
    clearTimeout(g.time);
    
    // 计算滑动角度
    let _angle = this.getAngle(_disX, _disY);
    
    // 判断滑动方向
    if (_dis > 18 && _timeDis < 300) {
      g.touchType === "swipe" && g.callBack(e, g.binding.value);
      if (_angle >= 60 && _angle <= 120)
        g.touchType === "swipedown" && g.callBack(e, g.binding.value);
      if (_angle <= -60 && _angle >= -120)
        g.touchType === "swipeup" && g.callBack(e, g.binding.value);
      if (_angle <= 20 && _angle >= -20)
        g.touchType === "swipeleft" && g.callBack(e, g.binding.value);
      if ((_angle <= -160 && _angle > -180) || (_angle >= 160 && _angle <= 180))
        g.touchType === "swiperight" && g.callBack(e, g.binding.value);
    } else {
      if (!g.longTouched && !g.moved) {
        g.touchType === "tap" && g.callBack(e, g.binding.value);
        g.leaved = true;
      }
    }
  }
  
  move() {
    this.moved = true;
  }
  
  /**
   * 获取点击集合的中心坐标
   * @param touches touch对象集合
   * @return {{x: number, y: number}}
   */
  getMultiCenter(touches) {
    
    let g = this, x = 0, y = 0;
    
    const _length = touches.length;
    
    for (let i = 0; i < _length; i++) {
      x += touches[i].pageX;
      y += touches[i].pageY;
    }
    
    return {
      x: Math.round(x / _length),
      y: Math.round(y / _length)
    };
  };
  
  /**
   * 获取滑动的角度
   * @param disX X轴的位移
   * @param disY Y轴的位移
   * @return 角度
   */
  getAngle(disX, disY) {
    let g = this;
    
    if (typeof disX !== "number" || typeof disY !== "number")
      return 45;
    
    return Math.atan2(disY, disX) * 180 / Math.PI;
  }
}

然后执行生成指令

Vue.directive("tap", {
    bind: function (el, binding) {
      new VueTouch(el, binding, "tap");
    }
  });
  Vue.directive("swipe", {
    bind: function (el, binding) {
      new VueTouch(el, binding, "swipe");
    }
  });
  Vue.directive("swipeleft", {
    bind: function (el, binding) {
      new VueTouch(el, binding, "swipeleft");
    }
  });
  Vue.directive("swiperight", {
    bind: function (el, binding) {
      new VueTouch(el, binding, "swiperight");
    }
  });
  Vue.directive("swipedown", {
    bind: function (el, binding) {
      new VueTouch(el, binding, "swipedown");
    }
  });
  Vue.directive("swipeup", {
    bind: function (el, binding) {
      new VueTouch(el, binding, "swipeup");
    }
  });
  Vue.directive("longtap", {
    bind: function (el, binding) {
      new VueTouch(el, binding, "longtap");
    }
  });

源码参看:https://github.com/LylaYuKako...

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

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

相关文章

  • vue移动体验上的优化解决方案

    摘要:去年年底自己搭了一个在移动端的开发框架,感觉体验不是很好。路由懒加载首页终于写完了,以上这些就是我在移动端体验优化的实战。去年年底自己搭了一个vue在移动端的开发框架,感觉体验不是很好。上个星期又要做移动端的项目了。所以我花了两天时间对之前的那个开发框架做了以下优化 自定义vuex-plugins-loading 路由切换动画 + keep alive 动态管理缓存组件 better-sc...

    godlong_X 评论0 收藏0
  • 微信开发之微信jssdk录音功能开发

    项目需求简单描述 用户长按录音,松手后直接结束录音,结束录音后,用户可以选择重新录音、播放刚才的录音,上传录音(这里的上传录音指上传到自己服务器,上传步骤是,前端调用wx.uploadVoice,后台再到微信服务器下载音频文件,上传到自己的服务器)。注意,音频文件自上传时间算起在微信服务器的有效期为3天。由于后台从微信服务器下载的音频文件是amr格式的,需要后台先把amr文件转换成MP3,前端用a...

    bingchen 评论0 收藏0
  • vue移动侧滑面板组件

    摘要:里边涉及到的指令是自定义的指令,为了处理移动端的点击操作,我还整理了一片陋文移动点击长按滑动指令然后这个组件的源码我放在了我出来的项目上谢谢各位品尝, 以下这段都是废话,请跳过 公司移动端开发平台进行了大变革,前端架构由DCloud大生态转换为VUE,所以移动端的UI组件库从MUI改为使用MintUI,然后开始大刀阔斧的把MintUI组件改成MUI组件的样子,然后发现少了几个较为常用的...

    TNFE 评论0 收藏0
  • 单点手势库

    摘要:单点手势库分析手势是什么有哪些方法实现首先我这里指的手势是指我们在移动端进行触屏交互的时候,用户操作的一些手势。可以看地址总结这是我挺久之前做的移动端单点手势库学习时参考剧中人你可以在这里找到我个人网站 单点手势库 分析 手势是什么? 有哪些方法实现? 首先我这里指的手势是指我们在移动端进行触屏交互的时候,用户操作的一些手势。在我们在移动端需要一些交互的时候。难免有时候需要左滑右...

    jollywing 评论0 收藏0

发表评论

0条评论

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