如何用JavaScript實現手勢庫

醉折花枝作酒筹
發布: 2021-05-07 09:25:32
轉載
2449 人瀏覽過

本篇文章為大家介紹JS實作日期比較大小的方法。有一定的參考價值,有需要的朋友可以參考一下,希望對大家有幫助。

如何用JavaScript實現手勢庫

Start 事件

#首先我們會觸發一個start 事件,也就是當我們手指觸摸到螢幕時第一個觸發的事件。這時會有三種情況:

  • 手指放開#
    • 會觸發end 事件,這樣就構成一個tap點選的行為
    • 透過監聽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 函數中加入

startXstartY的座標記錄,這裡要注意的是,因為這兩個值都會在多個地方被使用的,所以也是需要在全域作用域中聲明。

然後在 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事件就會被取消。所以這裡我們就需要clearTimeoutpressstarthandler給清楚掉。

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-endpan-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實現手勢庫的詳細內容。更多資訊請關注PHP中文網其他相關文章!

相關標籤:
來源:csdn.net
本網站聲明
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn
最新問題
最新下載
更多>
網站特效
網站源碼
網站素材
前端模板
關於我們 免責聲明 Sitemap
PHP中文網:公益線上PHP培訓,幫助PHP學習者快速成長!