移動端tap解決方案

移動端click事件有300ms延遲。
在Android上面可以通過以下兩種方式解決:

  1. 添加meta標(biāo)簽:
<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標(biāo)簽方式也不會出現(xiàn)延時, 據(jù)這里討論Fastclick is no longer required in iOS 10也沒有延遲了,但是在ios的UIWebView中還會有問題。
針對這種場景,可以試用fastclick來解決

fastclick思路是:

  1. 監(jiān)聽dom元素的 touchstarttouchmove 、touchend 事件;
  2. 在touchstart中記錄移動的元素,開始位置;
  3. 在touchmove中判斷移動端元素是否有變化,移動位置,如果超過閾值就認(rèn)為是滑動,不過進(jìn)一步處理。
  4. 在touchend中判斷是否需要點擊一個元素,還是發(fā)生了滑動事件,如果是點擊就構(gòu)造一個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,也可以開發(fā)一個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);
  }
}
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容