首頁  >  文章  >  web前端  >  如何用JavaScript實現手勢庫

如何用JavaScript實現手勢庫

醉折花枝作酒筹
醉折花枝作酒筹轉載
2021-05-07 09:25:322525瀏覽

本篇文章為大家介紹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刪除