移動端交互手勢詳解及實現


一丶概述  
    如今移動端設備大行其道,前端也走進了移動的領域。在寫移動端頁面的交互效果的時候,我么難免要接觸一些復雜的手勢,而不僅僅像pc端那樣簡單的鼠標事件。手勢實際上是一種輸入模式。我們現在在直觀意義上理解的人機交互是指人與機器之間的互動方式,這種互動方式經歷了鼠標、物理硬件、屏幕觸控、遠距離的體感操作的逐步發展的過程。

二丶移動端手勢事件  
    在瀏覽器中,為我們提供的手勢並不算多,主要有:

  1.         touchstart 當手指觸摸屏幕時觸發
  2.         touchmove 當手指在屏幕滑動時不斷的觸發
  3.         touchend 當手指從屏幕上移開時觸發
  4.         touchcancel 當系統停止跟蹤觸摸時觸發

    是不是感覺很少,safari還為我們提供了三個獨有的手勢事件(用於復雜的手勢),然而也僅僅只能在safari中使用  

  1.         gesturestart 當一個手指已經按在屏幕上,另一個手指也按上時觸發
  2.         gesturechange 當觸摸屏幕上任何一個手指發生變化時觸發
  3.         gestureend 當任何一個手指從屏幕上移開時觸發

  gesture支持情況  
    最后呢,讓我們看看移動設備上究竟有哪兒手勢需要我們使用  
移動端交互手勢詳解及實現  

三丶讓JS支持這些手勢  
    目前看來,我們能用的也就只有touchstart,touchmove, touchend, touchcancel這四個手勢,那么如何才能利用這四個手勢支持眾多的交互效果呢?首先我們從最簡單的手勢開始。簡單的手勢也就是說是單點觸控,我們主要來實現如下幾個手勢:

  1.        tap 輕觸
  2.        doubletap 連續兩次輕觸
  3.        press 長按
  4.        pan 平移
  5.        flick 輕拂

  
    首先我們要解決如何觸發自定義事件(已經了解自定事件的可以跳過):  
   

//自定義一個事件
document.body.addEventListener("tap", function(event) {
console.log("tap事件觸發")
}, false)
//觸發自定義事件
function fireEvent(element, type, extra) {
var event = doc.createEvent('HTMLEvents');
event.initEvent(type, true, true);
if (typeof extra === 'object') {
Util.extends(event, extra); //淺拷貝
}
element.dispatchEvent(event);
}
fireEvent(document.body, "tap", {}); //觸發tap事件

我們在整個事件模擬中定義一個中間狀態 evet.status 來表示當前的觸摸狀態,接下來我們就利用touchstart,touchmove,touchend來可以實現自己的觸摸事件了   
    tap事件:當touchstart觸發時,我們將event.status狀態改為 tapping。在touchend觸發時,如果event.status依然為tapping則,觸發tap事件。  
    doubletap事件:在觸發tap事件的時候,我們用一個變量lastTime記錄當前時間。下一次觸發tap時,用當前時間和lastTime做對比,如果小於300ms則觸發doubletap事件  
    pess事件:當touchstart觸發時,我們定義一個setTimeout的函數(500ms),如果500ms后仍然沒有touchend觸發,則定時函數將event.staus狀態改為pressing。當touchend觸發時,檢測到狀態為pressing則觸發press事件。  
    pan事件:我們在touchmove中檢測當前狀態是tapping和pressing時,並且手指移動距離大於10px則,觸發pan平移事件。這個移動距離用event.touches[0].clientX - lastTouch.clientY 來檢測就好(利用lastTouch記錄,起始手指的event對象)。  
    flick事件:這個事件就是"刷~刷"的划過屏幕的交互效果,在touchend時通過pan事件的移動距離和移動事件算出速度(注意是X和Y軸的合速度),如果速度大於0.5,並且整個觸摸過程時間小於100ms,則觸發flick事件。  
     是不是很簡單的用最原始的瀏覽器事件就能實現這些內容。  
     接下來讓我們看看兩個手指的事件如果實現。  

四丶實現多指觸控 
    在實現多指觸控的時候,我們需要了解一下觸摸過程中event用來保存多個手指信息的三個屬性:

  1.        touches當前屏幕上所有觸摸點的集合列表
  2.        targetTouches綁定事件的那個結點上的觸摸點的集合列表
  3.        changedTouches觸發事件時改變的觸摸點的集合

  
     這三個有什么區別?舉例來說,比如div1, div2只有div2綁定了touchstart事件,第一次放下一個手指在div2上,觸發了touchstart事件,這個時候,三個集合的內容是一樣的,都包含這個手指的touch,然后,再放下兩個手指一個在div1上,一個在div2上,這個時候又會觸發事件,但changedTouches里面只包含第二個第三個手指的信息,因為第一個沒有發生變化,而targetTouches包含的是在第一個手指和第三個在div2上的手指集合,touches包含屏幕上所有手指的信息,也就是三個手指。這樣是不是就很很清楚了。下面我們要根據上面的內容,繼續解決一個問題:當兩個手指作用在不同的節點上應該觸發哪個節點的事件呢?
    這里我們規定,如果觸發在了兩個不同節點上,我們去兩個節點公有的最近父節點,作為觸發的目標。尋找共有最小父節點代碼如下:

//判斷節點ele1是否包含ele2
function contains(ele1, ele2) {
return ele1.contains ? ele1 != ele2 && ele1.contains(ele2) : !!(ele1.compareDocumentPosition(ele2) & 16);
}
//獲得共有最近的父節點
function getCommonRootNode(ele1, ele2) {
while (ele1) {
if (contains(ele1, ele2) || ele1 === ele2) {
return ele1;
}
ele1 = ele1.parentNode;
}
return null;
}

這樣我們解決了,如何找到多個手指信息和觸發哪個節點的問題。最后一個問題,當給了我們這些信息我們怎么能用?比如計算旋轉手勢,縮放手勢啊什么的。  
    這里我們僅考慮兩個手指的多點觸控。我們設touchstart階段的兩個手指坐標為 A(x1, y1)   B(x2, y2)。touchmove過程中的兩個手指的坐標為 C(x3, y3) D(x4, y4)。
    rotate旋轉:計算AB,CD線段與坐標軸的夾角,對角度相減即得到旋轉角度。  
    scale 縮放:計算AB線段長度和CD線段長度(勾股定理),兩條線段做比值就好。  
    translate平移: 平移的話我們只計算A點到C點的x坐標變化量。 
    具體代碼如下:

function calcAction(x1, y1, x2, y2, x3, y3, x4, y4) {
let rotate = Math.atan2(y4 - y3, x4 - x3) - Math.atan2(y2 - y1, x2 - x1),
scale = Math.sqrt((Math.pow(y4 - y3, 2) + Math.pow(x4 - x3, 2)) / (Math.pow(y2 - y1, 2) + Math.pow(x2 - x1, 2))),
translate = [x3 - scale * x1 * Math.cos(rotate) + scale * y1 * Math.sin(rotate), y3 - scale * y1 * Math.cos(rotate) - scale * x1 * Math.sin(rotate)];
return {
rotate: rotate,
scale: scale,
translate: translate,
/**
* |ax + cy + e|
* |bx + dy + f|
* | 0 + 0 + 1|
*/
martrix: [
[scale * Math.cos(rotate), -scale * Math.sin(rotate), translate[0]],
[scale * Math.sin(rotate), scale * Math.cos(rotate), translate[1]],
[0, 0, 1]
]
}
}

了解這些內容,你就可以在touchmove過程中完成對兩個手指的旋轉縮放平移等交互效果進行封裝了。是不是很簡單呢!  
     當然完整的事件過程要分start,move,end這三種情況,在實現的時候要分別給予對應的實現就可以了。都逃不開對touchstart,touchmove,touchend的利用。  
五丶實現案例
    基於上面的方案,我實現了一個對移動端手勢的封裝庫,包含以上所有的手勢。猶豫這里代碼運行不能模擬手機環境,我就不貼代碼了。  
    感興趣的同學可以訪問:https://github.com/T-phantom/si-gesture  上面有具體的使用方法和帶有詳細注釋的源碼哦,歡迎start。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM