美文网首页
移动端tap解决方案

移动端tap解决方案

作者: 无米学炊 | 来源:发表于2018-11-15 09:57 被阅读28次

    移动端click事件有300ms延迟。
    在Android上面可以通过以下两种方式解决:

    1. 添加meta标签:
    <meta name="viewport" content="width=device-width">
    
    1. 使用css属性 touch-action
    html {
        touch-action: manipulation;
    }
    

    方案来源300ms tap delay, gone away
    测试链接:

    1. 有延迟页面
    2. 无延迟-meta
    3. 无延迟-css方式

    在最新版本的ios12上面使用 meta标签方式也不会出现延时, 据这里讨论Fastclick is no longer required in iOS 10也没有延迟了,但是在ios的UIWebView中还会有问题。
    针对这种场景,可以试用fastclick来解决

    fastclick思路是:

    1. 监听dom元素的 touchstarttouchmovetouchend 事件;
    2. 在touchstart中记录移动的元素,开始位置;
    3. 在touchmove中判断移动端元素是否有变化,移动位置,如果超过阈值就认为是滑动,不过进一步处理。
    4. 在touchend中判断是否需要点击一个元素,还是发生了滑动事件,如果是点击就构造一个MouseEvent
      clickEvent = document.createEvent('MouseEvents');
       clickEvent.initMouseEvent('click', true, true, window, 1, touch.screenX, touch.screenY, touch.clientX, touch.clientY, false, false, false, false, 0, null);
       clickEvent.forwardedTouchEvent = true;
       targetElement.dispatchEvent(clickEvent);
      

    基于这种思路,如果使用vue,也可以开发一个vue的指令:

    const ua = navigator.userAgent.toLowerCase();
    const isIos = /ios|iphone|ipod|ipad/.test(ua);
    
    const instances = [];
    
    export default function install (Vue) {
      Vue.directive('tap', {bind, unbind});
    }
    // 安卓通过添加 <meta name="viewport" content="width=device-width"> 不会有300ms的延迟
    function isAvailable (modifiers) {
      return modifiers.all || modifiers.press || isIos;
    }
    
    function bind (node, directive) {
      const modifiers = directive.modifiers;
      const useTap = directive.value !== false;
      if (isAvailable(modifiers) && useTap) {
        instances.push(new Tap(node, modifiers));
      }
    }
    
    function unbind (node, directive) {
      const modifiers = directive.modifiers;
    
      if (isAvailable(modifiers)) {
        let i = 0;
        let l = instances.length;
        for (; i < l; i++) {
          if (instances[i].node === node) {
            instances[i].handler(false);
            break;
          }
        }
        if (i !== l) {
          instances.splice(i, 1);
        }
      }
    }
    
    const CONSTANTS = {
      tapMove: 8,
      tapTime: 500
    };
    
    class Tap {
      constructor (node, modifiers) {
        this.node = node;
        this.triggerPress = modifiers.press;
        this.stopPropagation = modifiers.stop;
    
        this.handler(true);
      }
    
      handler (flag) {
        const action = flag ? 'addEventListener' : 'removeEventListener';
        this.node[action]('touchstart', this.start.bind(this));
        this.node[action]('touchmove', this.move.bind(this));
        this.node[action]('touchend', this.end.bind(this));
        this.node[action]('touchcancel', this.end.bind(this));
      }
    
      start (e) {
        this.stop(e);
        this.moved = false;
    
        this.coords = {
          x: e.touches[0].clientX,
          y: e.touches[0].clientY
        }
    
        if (this.triggerPress) {
          this.pressTimer = setTimeout(() => {
            if (!this.moved) {
              this.pressTrigger = true;
              this.trigger('press');
            }
          }, CONSTANTS.tapTime);
        }
      }
    
      move (e) {
        this.stop(e);
        const movedDistance = Math.max(
          Math.abs(e.touches[0].clientX - this.coords.x),
          Math.abs(e.touches[0].clientY - this.coords.y)
        );
        // 如果移动距离大于8
        this.moved = this.moved || movedDistance > CONSTANTS.tapMove;
      }
    
      end (e) {
        this.stop(e);
        e.cancelable && e.preventDefault && e.preventDefault();
    
        if (this.pressTrigger) {
          this.pressTrigger = false;
        } else {
          if (this.triggerPress) {
            clearTimeout(this.pressTimer);
          }
          if (!this.moved) {
            this.trigger('click');
          }
        }
      }
    
      stop (e) {
        if (e && e.stopPropagation && this.stopPropagation) {
          e.stopPropagation();
        }
      }
    
      trigger (eventName) {
        let event;
        if ('CustomEvent' in window) {
          event = new CustomEvent(eventName);
        } else {
          event = document.createEvent('CustomEvent');
          event.initCustomEvent(eventName, true, true, void 0);
        }
        this.node.dispatchEvent(event);
      }
    }
    

    相关文章

      网友评论

          本文标题:移动端tap解决方案

          本文链接:https://www.haomeiwen.com/subject/xwmefqtx.html