参考文章:
在手持设备上使用 touchstart 事件代替 click 事件是不是个好主意
移动端兼容问题研究 javascript事件机制详解(涉及移动兼容)
读fastclick源码有感 彻底解决tap“点透”,提升移动端点击响应速度
实现一个移动端TODO应用,想添加一些触摸事件,之前无论PC还是移动端,都统一用click,现在细思恐极啊~~。一时半会想不出更好的实现方案,于是找了很多资料学习了一下,在此做个分享。
为什么移动端不用click
估计大家都知道,移动端的click有300ms延迟的问题,在移动端浏览器中,连续两次点击是缩放操作,所以在用户点击屏幕后,浏览器会检查在300ms内是否有另一次点击,如果没有则触发click事件。因为有延迟,所以不使用咯。
替换click的方案
- 使用touchstart
touch事件包括touchstart、touchend、touchmove等,简单使用touchstart来替换click,但问题是,如果我想在同一对象上绑定一个单击事件和一个滑动事件怎么办,这时候就会出现冲突。 - 使用tap事件
标准事件中没有tap事件,tap事件是一些库,如zepto,使用touch进行封装的,在touchstart、touchend时记录时间、手指位置,在touchend时进行比较,如果手指位置为同一位置且时间间隔较短,且过程中未曾触发touchmove事件,则调用回调函数。
zepto用于判断的关键代码:
//延迟时间
if (delta > 0 && delta <= 250) touch.isDoubleTap = true;
//允许的偏移量
if (deltaX < 30 && deltaY < 30) {……}
不过300ms后还是会产生click事件,只是在该对象上没有进行监听,由此产生了“点透”现象:
(1)页面弹出一个模态框,模态框上有个按钮(关闭模态框),按钮正下方(在主页面上)有一个输入框
(2)当点击模态框上的关闭按钮,模态框立即消失,但300ms后click事件触发,而输入框正好监听click事件,因此输入框会得到焦点
为什么不对click进行拦截呢?原因是zepto使用的是事件代理,元素上的touch事件冒泡到document上,在document的事件回调中执行绑定在元素上的事件回调,而在document上执行preventDefault是不起作用的。
解决“点透”问题:略。
- 使用fastclick
使用方法参考:https://github.com/ftlabs/fastclick
fastclick也是使用事件委托(FastClick.attach(document.body, options);),但是冒泡到body上后,通过tap原理来判断是否单击,是则重新构造一个点击事件dispatch到原来的元素上,触发元素上的绑定事件,由于在tap事件后触发click,所以解决了延迟问题,并且300ms后,不再产生click事件,所以解决了”点透“问题。
FastClick.prototype.sendClick = function(targetElement, event) {
……
clickEvent = document.createEvent('MouseEvents');
clickEvent.initMouseEvent(this.determineEventType(targetElement),
true, true, window, 1, touch.screenX, touch.screenY, touch.clientX, touch.clientY, false, false, false, false, 0, null);
clickEvent.forwardedTouchEvent = true;
targetElement.dispatchEvent(clickEvent);
};
另外的好处是
FastClick doesn't attach any listeners on desktop browsers.
Chrome 32+ on Android withwidth=device-width in the viewport meta tag doesn't have a 300ms delay
到了这里,我们知道,最好的解决兼容PC和移动端的点击事件的办法是引入fastclick。然而我现在想要实现的是各种触摸事件——gg——既然要实现各种触摸事件,那就暂时不考虑PC的问题,所以考虑使用hammer.js
hammer.js
使用hammer.js实现qq消息中拖出删除画面的效果
最开始的实现方法是通过panstart、panmove、panend来实现滑动条的滑动距离和手指的滑动成相等关系,实现方法可以看我的codepan:
See the Pen <a href="http://codepen.io/jsAllen/pen/WGdBgN/">touch delete</a> by jsAllen (<a href="http://codepen.io/jsAllen">@jsAllen</a>) on <a href="http://codepen.io">CodePen</a>
不过这样的动画实现过程比较恶心,有好多的判断,后来在vue项目中,直接改用过渡,通过panleft、panright来修改translateX,再给tranlateX添加过渡。虽然效果明显弱了点,但操作流畅了许多。
在vue中使用hammer.js
在github上找了一下,发现有个vue-touch,更惊喜的是,这是个官方插件,不过项目使用的是vue2.0版本,而vue-touch暂不兼容vue2.0。看了一下issue,发现有人也遇到这个问题,然后又发现vue-touch有一个分支,已经做了兼容,哈哈。看了下源码,其实就是将hammer.js封装成指令。
使用方法:
<div v-touch:panleft="onPanLeft" v-touch:panright="onPanRight">
就此解决了项目中的一个小小触摸事件,不过感觉后面的坑会很多,自己接触移动端事件比较少,所以后面还要不断的总结经验,寻找更加高效优雅的处理方法。