移动端click事件有300ms延迟。
在Android上面可以通过以下两种方式解决:
- 添加
meta
标签:
<meta name="viewport" content="width=device-width">
- 使用css属性
touch-action
:
html {
touch-action: manipulation;
}
方案来源300ms tap delay, gone away
测试链接:
在最新版本的ios12上面使用 meta
标签方式也不会出现延时, 据这里讨论Fastclick is no longer required in iOS 10也没有延迟了,但是在ios的UIWebView中还会有问题。
针对这种场景,可以试用fastclick来解决
fastclick思路是:
- 监听dom元素的
touchstart
、touchmove
、touchend
事件; - 在touchstart中记录移动的元素,开始位置;
- 在touchmove中判断移动端元素是否有变化,移动位置,如果超过阈值就认为是滑动,不过进一步处理。
- 在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);
}
}
网友评论