笔记:移动端 touch 事件(二)模拟 swipe 的实现


前言

前端开发中没有原生的 swipe 事件,而一般库里比如 zepto.js 中的 touch.js,或者 Hammer.js,都是通过原生的 touch 事件 touchstarttouchmovetouchendtouchcancel 结合在一起通过各种计算来达到 swipeupswipedownswipeLeftswipeRight 的自定义事件的效果。

最近的工作内容一直在处理这一块,所以记录一下原理、思路和一般实现。


Touch Event 对象及其属性

e.touches,e.targetTouch,e.changedTouches

Touch Event 事件的 Event Object,包含触摸点内容的有三个信息集合:touchestargetToucheschangedTouches

属性 说明 MDN 地址
e.touches 所有接触到屏幕的触摸点的信息集合 链接
e.targetTouches 绑定事件的元素上所有触摸点的信息集合 链接
e.changedTouches 触屏事件发生改变的触摸点的信息集合 链接

帮助理解的例子

  1. 屏幕上只有一个触摸点,一般情况下就是在屏幕上一个手指在移动的时候,touchestargetToucheschangedTouches 中的信息是一致的。这个时候随着手指移动,信息集合在 changedTouches 中;
  2. 当放下第二个手指,touches 中会有两个触摸点信息集合,一个集合对应一个接触点的信息。如果这个时候第二个接触点所在的元素和第一个接触点所在的元素一致,targetTouches 就会有两个信息集合,反之则没有。changedTouches 会有第二个触摸点的信息,因为的增加的第二个触摸点造成了第二次触摸事件;
  3. 在两个手指同时触摸到屏幕上,造成同时有两个触摸点的时候,changedTouches 中是可能会有两个触摸点信息集合;
  4. 如果手指在屏幕上(造成的触摸点)移动,会变化的触摸点信息集合只会是 changedTouches,其中会包含所有触摸点的信息集合(至少会有一个);
  5. 多个触摸点在屏幕上,一个触摸点消失(从屏幕上移开一个手指),这个触摸点的集合信息会从 touchestargetTouches 中消失,但是会在 changedTouches 中出现;
  6. 多个触摸点在屏幕上,当最后一个触摸点消失之后,touchestargetTouches 都会变成空,不包含任何触目点信息集合。但是 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.changedTouchese.touches 也没问题) 来获取正在移动的触摸点(手指)的移动信息,根据不同情况,通过将 touchstarttouchend 两个时间点触摸信息中的 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.

参考

感谢阅读

你们好, 2018 年初把小站从 Jekyll 迁移到 Hugo 的过程中,删除了评论区放的 Disqus 插件,考虑有二:首先无论评论、还是对笔记内容的进一步讨论,读者们更喜欢通过邮件、或者 Twitter 私信的方式来沟通;其次一年多以来 Disqus 后台能看到几乎都是垃圾留言(spam),所以这里直接贴一下邮件、以及 Twitter 账户 地址。

技术发展迭代很快,所以这些笔记内容也有类似新闻的时效性,不免有过时、或者错误的地方,欢迎指正 ^_^。

BEST
Lien(A.K.A 胡椒)
本站总访问量 本站总访客量 本文总阅读量