如何用JavaScript实现手势库
Start 事件
首先我们会触发一个 start 事件,也就是当我们手指触摸到屏幕时第一个触发的事件。这时会有三种情况:
-
手指松开
- 会触发 end 事件,这样就构成一个
tap
点击的行为 - 通过监听 end 事件来实现即可
- 会触发 end 事件,这样就构成一个
-
手指拖动超过 10 px
- 这种就是
pan start
拖动的行为 - 我们可以在 move 事件判断当前与上一个触点的距离
- 这种就是
-
手指停留在当前位置超过 0.5s
- 这种就是
press start
按压的行为 - 我们可以添加一个 setTimeout 来实现
- 这种就是
Press 事件
所以我们第一步就是在 start
函数中加入一个 setTimout
的 handler 处理程序。
let handler;let start = point => { handler = setTimeout(() => { console.log('presss '); }, 500);};
一般来说 press
是我们比较常见的一个行为。但是实际上这里是 press start 事件,后面还会跟随着一个 press end 的事件。我们也可以统称这个为 press
事件,然后这个手势库的使用者只需要监听这个 press
事件即可,极少的情况下是需要监听 press end
事件的。
这里我们需要注意的是,当我们触发其他的事件的时候,这个 500 毫秒的 setTimout 是有可能会被取消掉的。所以我们需要给这段逻辑一个 handler
,并且放在全局作用域中,让其他事件可以获取到这个变量,并且可使用它取消掉这个处理逻辑。
Pan 事件
接下来我们就去监听移动 10px 的 pan
事件,这里就需要我们记录一开始用户触摸屏幕时的 x 和 y 坐标,当用户移动手指的时候,持续计算新移动到的位置与初始位置的距离。如果这个距离超过了 10px 就可以触发我们的 pan start
的事件了。
所以首先我们需要在 start 函数中加入 startX
和 startY
的坐标记录,这里要注意的是,因为这两个值都是会在多个地方被使用的,所以也是需要在全局作用域中声明。
然后在 move 函数中计算当前触点与起点的直径距离。这里我们需要用到数学中的直径运算公式 z 2 x^2 + y^2 = z^2x 2 +y 2 =z 2 ,而这里面的 x 是 当前触点的 x 坐标 - 起点的 x 坐标 的 x 轴的距离, y 就是 当前出点的 y 坐标 - 起点的 y 坐标 运算出来的 y 轴的距离。最终两个距离二次幂相加就是直径距离的二次幂。
在代码中我们一般都会尽量避免使用根号运算,因为根号运算会对性能有一定的影响。我们知道最终要判断的是直径距离是否是大于一个固定的 10px。那就是说 z = 10,而 z 的二次幂就是 100,所以我们直接判断这个直径距离是否大于 100 即可。
这里还有一个需要注意的,就是当我们手指移动超过 10px 之后,如果我们手指没有离开屏幕而是往回移动了,这样的话我们距离起点已经不够 10px了。但是这个其实也是算 pan 事件,因为我们确实有移动超过 10px 距离,超过这个距离之后所有的移动都是属于 pan 事件。
所以我们需要一个 isPan
的状态,第一次移动超出 10px 的时候,就会触发 pan-start
事件,并且把 isPan
置为 true,而后面的所有移动都会触发 pan
事件。
根据我们上面讲到的 press
事件,如果我们按下手指后 0.5 秒内出现了移动,那么 press
事件就会被取消。所以这里我们就需要 clearTimeout
把 pressstart
的 handler
给清楚掉。
let handler;let startX, startY;let isPan = false;let start = point => { (startX = point.clientX), (startY = point.clientY); isPan = false; handler = setTimeout(() => { console.log('pressstart'); }, 500);};let move = point => { let dx = point.clientX - startX, dy = point.clientY - startY; let d = dx ** 2 + dy ** 2; if (!isPan && d > 100) { isPan = true; console.log('pan-start'); clearTimeout(handler); } if (isPan) { console.log(dx, dy); console.log('pan'); }};
Tap 事件
Tap 的这个逻辑我们可以在 end 事件里面去检查。首先我们默认有一个 isTap
等于 true 的状态,如果我们触发了 pan 事件的话,那就不会去触发 tap 的逻辑了,所以 tap 和 pan 是互斥的关系。但是为了不让它们变得很耦合,所以我们不使用原有的 isPan 作为判断状态,而是另外声明一个 isTap
的状态来记录。
这里我们 tap 和 pan 都有单独的状态,那么我们 press 也不例外,所以也给 press 加上一个 isPress
的状态,它的默认值是 false。如果我们 0.5 秒的定时器被触发了,isPress
也就会变成 true。
既然我们给每个事件都加入了状态,那么这里我们就给每一个事件触发的时候设置好这些状态的值。
-
press 时
- isTap = false
- isPan = false
- isPress = true
-
pan 时
- isTap = false
- isPan = true
- isPress = false
-
tap 时
- isTap = true
- isPan = false
- isPress = false
如果我们发现用户没有移动,也没有按住触屏超过 0.5 秒,当用户离开屏幕时就会调用 end 函数,这个时候我们就可以认定用户的操作就是 tap。这里我们要注意的是,我们 press 的 0.5 秒定时器是没有被关闭的,所以我们在 isTap 的逻辑中需要 clearTimeout(handler)
。
说到取消 press 定时器,其实我们 handler 的回调函数中,也需要做一个保护代码逻辑,在触发了 press-start 之后,我们需要保证每次点击屏幕只会触发一次,所以在 setTimout 的回调函数中的最后,我们需要加上 handler = null
。这样只要 press-start 触发了,就不会再被触发。
let handler;let startX, startY;let isPan = false, isPress = false, isTap = false;let start = point => { (startX = point.clientX), (startY = point.clientY); isPan = false; isTap = true; isPress = false; handler = setTimeout(() => { isPan = false; isTap = false; isPress = true; console.log('press-start'); handler = null; }, 500);};let move = point => { let dx = point.clientX - startX, dy = point.clientY - startY; let d = dx ** 2 + dy ** 2; if (!isPan && d > 100) { isPan = true; isTap = false; isPress = false; console.log('pan-start'); clearTimeout(handler); } if (isPan) { console.log(dx, dy); console.log('pan'); }};let end = point => { if (isTap) { console.log('tap'); clearTimeout(handler); }};
End 事件
到了最后这里我们要处理的就是所有的结束时间,包括 press-end
和 pan-end
。
这两个 end 事件都会在 end 函数中判断所得,如果在用户操作的过程中触发了 pan-start
或者 press-start
事件,到了 end 函数这里,对应的状态就会是 true。
所以我们对 end 函数做了以下改造:
let end = point => { if (isTap) { console.log('tap'); clearTimeout(handler); } if (isPan) { console.log('pan-end'); } if (isPress) { console.log('press-end'); }};
最后我们需要在 cancel 事件触发的时候,清楚掉 press 事件的 setTimeout。既然我们的操作被打断了,那也不可能会触发我们的长按事件了。
// 加入 cancellet cancel = point => { clearTimeout(handler); console.log('cancel');};
我们除了 flick
的逻辑,我们已经完成所有手势库里面的事件了。并且也能正确的区分这几种手势操作了。
【推荐学习:javascript高级教程】
以上就是如何用JavaScript实现手势库的详细内容,更多请关注其它相关文章!
上一篇: mysql怎么导出数据
下一篇: 怎么用cmd进入mysql
推荐阅读
-
如何用JavaScript实现功能齐全的单链表详解
-
如何用JavaScript实现功能齐全的单链表详解
-
oracle如何用expdp定时备份?oracle用expdp实现定时备份数据库图文详细教程
-
JavaScript开发跨域问题:如何用jQuery实现跨域
-
javascript原生和jquery库实现iframe自适应高度和宽度
-
JavaScript实现汉字转换为拼音的库文件示例
-
聊聊那些使用前端Javascript实现的机器学习类库
-
如何用JavaScript实现一个数组惰性求值库
-
javascript使用CryptoJS库实现AES加密
-
JavaScript开发跨域问题:如何用jQuery实现跨域