前言
前端开发中没有原生的 swipe 事件,而一般库里比如 zepto.js 中的 touch.js,或者 Hammer.js,都是通过原生的 touch 事件 touchstart
、touchmove
、touchend
、touchcancel
结合在一起通过各种计算来达到 swipeup
、swipedown
、swipeLeft
、swipeRight
的自定义事件的效果。
最近的工作内容一直在处理这一块,所以记录一下原理、思路和一般实现。
Touch Event 对象及其属性
e.touches,e.targetTouch,e.changedTouches
Touch Event 事件的 Event Object
,包含触摸点内容的有三个信息集合:touches
、targetTouches
和 changedTouches
。
属性 | 说明 | MDN 地址 |
---|---|---|
e.touches |
所有接触到屏幕的触摸点的信息集合 | 链接 |
e.targetTouches |
绑定事件的元素上所有触摸点的信息集合 | 链接 |
e.changedTouches |
触屏事件发生改变的触摸点的信息集合 | 链接 |
帮助理解的例子
- 屏幕上只有一个触摸点,一般情况下就是在屏幕上一个手指在移动的时候,
touches
,targetTouches
,changedTouches
中的信息是一致的。这个时候随着手指移动,信息集合在changedTouches
中; - 当放下第二个手指,touches 中会有两个触摸点信息集合,一个集合对应一个接触点的信息。如果这个时候第二个接触点所在的元素和第一个接触点所在的元素一致,
targetTouches
就会有两个信息集合,反之则没有。changedTouches
会有第二个触摸点的信息,因为的增加的第二个触摸点造成了第二次触摸事件; - 在两个手指同时触摸到屏幕上,造成同时有两个触摸点的时候,
changedTouches
中是可能会有两个触摸点信息集合; - 如果手指在屏幕上(造成的触摸点)移动,会变化的触摸点信息集合只会是
changedTouches
,其中会包含所有触摸点的信息集合(至少会有一个); - 多个触摸点在屏幕上,一个触摸点消失(从屏幕上移开一个手指),这个触摸点的集合信息会从
touches
、targetTouches
中消失,但是会在changedTouches
中出现; - 多个触摸点在屏幕上,当最后一个触摸点消失之后,
touches
、targetTouches
都会变成空,不包含任何触目点信息集合。但是changedTouches
会包含最后消失点的信息集合。
参考:
Touch 信息集合中的属性
属性 | 描述 | MDN 地址 |
---|---|---|
Touch.screenX |
触点相对于屏幕左边沿的的X坐标. 只读属性. | 链接 |
Touch.screenY |
触点相对于屏幕上边沿的的Y坐标. 只读属性. | 链接 |
Touch.pageX |
触点相对于HTML文档左边沿的的X坐标. 当存在水平滚动的偏移时, 这个值包含了水平滚动的偏移. 只读属性. | 链接 |
Touch.pageY |
触点相对于HTML文档上边沿的的Y坐标. 当存在水平滚动的偏移时, 这个值包含了垂直滚动的偏移. 只读属性. | 链接 |
Touch.clientX |
触点相对于可见视区(visual viewport)左边沿的的X坐标. 不包括任何滚动偏移. 只读属性. | 链接 |
Touch.clientY |
触点相对于可见视区(visual viewport)上边沿的的Y坐标. 不包括任何滚动偏移. 只读属性. | 链接 |
实现 swipe 的原理
实现 swipe 的原理其实不复杂的,前言中说道手指在屏幕上滑动(swipe)的监听是基于这几个触摸点的信息集合计算而出的,具体需要计算的就是根据不同的情况取得适当的 Touch
信息集合、以及其中适当的坐标位置。
判定 swipe
假设单个手指滑动,就使用 e.changedTouches
(e.touches
也没问题) 来获取正在移动的触摸点(手指)的移动信息,根据不同情况,通过将 touchstart
和 touchend
两个时间点触摸信息中的 pageX/Y
的值进行相减,得到手指在屏幕上移动的位移绝对值,以一个阈值对比衡量这个位移是否可以被判定为一个滑动操作,比如超过从右向左超过 30 像素距离,那就是一个滑动操作。
伪代码:
判定 swipe 方向
如果判定成了一个 swipe,接下来就需要对于 swipe 方向的判定,原理同样也和触摸点在 x、y 轴的位移相关,不过在这里是比较两者的大小,x 轴的位移待遇 y 轴的位移,是一个横向的 swipe,反之则是纵向。
之后,在判定了是横向还是纵向的 swipe 之后,位移绝对值的用处就到此为止,需要返回去: 如果是横向的 swipe,比较 endPos.x - startPos.x
是否大于零?向右 swipe : 向左 swipe;如果是纵向的 swipe,比较 endPos.y - startPos.y
是否大于零?向下 swipe : 向上 swipe。
封装
使用 pub/sub 继承
demo(需要在移动端设备中打开):
See the Pen mobile swipe using Pub/Sub by Lien (@movii) on CodePen.
pub/sub 使用的是 JavaScript 设计模式中「发布 - 订阅」来实现目标对手势动作的监听。为了方便复用,首先需要一个简单的「发布 - 订阅」和extend
;
其次是手势本身的封装,使用 👆 的 extend()
方法将 pub/sub 两个方法继承到自己身上。
具体的调用不复杂,其实是
使用 document.createEvent 来 dispatch CustomEvent
demo(需要在移动端设备中打开):
See the Pen mobile swipe using CustomEvent by Lien (@movii) on CodePen.
参考
- MDN 上关于的 Touch 对象文档
- Introduction to Touch events in JavaScript
- Detect left/right-swipe on touch-devices, but allow up/down-scrolling
- Variation of e.touches, e.targetTouches and e.changedTouches
技术发展迭代很快,所以这些笔记内容也有类似新闻的时效性,不免有过时、或者错误的地方,欢迎指正 ^_^。
BEST
Lien(A.K.A 胡椒)