移動開發框架剖析(二) Hammer專業的手勢控制


瀏覽器底層並沒有給元素提供類似,單擊,雙擊,滑動,拖動這些直接可以用的控制接口,一切的手勢動作都只能通過模擬出來。移動端瀏覽器唯一給我們提供的就只是mousedown -> mousemove -> mouseup三種最基本的事件接口。那么我們只能通過這些簡單的接口模擬出復雜的手勢出來。

常規的做法流程:

1.給元素上綁定三個事件,mousedown ,mousemove,mouseup

2.在交互的時候,用戶只觸發mousedown,mouseup沒有觸發mousemove,就可以單算是一次點擊的動作, 這里可以是 單擊,雙擊與長按,具體可以通過間隔的時間判斷

3.如果mousemove觸發了,自然就是滑動與拖動了

當然手勢還要涉及到多點操作縮放與旋轉的處理,就之后在討論。

 


先拋開流程,我們要知道設計一個手勢庫需要考慮的問題,至少有2點:

1.運行的平台

2.用戶的手勢

 

那么我們可以總結市面上的終端設備有那么三種:

1 手機/pad移動端

2 pc類

3 還有種帶觸摸屏的電腦一類

 

用戶的手勢行為大體分:

單擊tap , 雙擊doubletap,平移pan,滑動swipe,長按press,縮放pinch,旋轉rotate

 


從設計的角度來講,有着不同的兼容與選擇問題,卻又有着一些相同的共性與處理我們要如何去組織結構?

當然依舊是OOP設計了,抽出父類,實現繼承,引入策略模式

我們看看Hammer在結構上是如何實現這類設計的

常規來說手勢的處理,要分為初始化與執行期。初始化的時候構建所有相關的參與與方法

hammer源碼里面分幾大塊:

1. Hammer類,一個簡單的工廠方法,用來創建一個管理和初始化默認的識別器。Hammer.defaults配置一些基本的選項

包括針對每種識別器的配置與元素CSS屬性的設置

2. Manager類,整個庫的管理類。內部初始化了input輸入對象,所有手勢對象,元素css設置對象touchAction

3. InputHandler類,事件回調的具體加工類,用來生成包裝后的事件對象與派發事件到每一個識別器

4. TouchAction類,設置元素的touchAction屬性

5. Input類,事件處理類。用來處理綁定與銷毀,事件句柄的回調。每一個輸入類都需要繼承

6. Recognizer類,所有識別器需要繼承的基類

以上就是整個庫的類塊了,當然5與6都是屬於基類繼承的,在代碼運行的時候就自動構建完畢了

 


關於繼承inherit方法

hammer用的是傳統的類似繼承

function inherit(child, base, properties) {
    var baseP = base.prototype,
        childP;

    childP = child.prototype = Object.create(baseP);
    childP.constructor = child;
    childP._super = baseP;

    if (properties) {
        extend(childP, properties);
    }
}

只繼承了原型的方法,因為原型都是共享的,如果放置屬性可以被任何一個繼承的子類所有修改,所以屬性的繼承需要用call方法

繼承的子類有一個私有屬性 _super指向父類,同時還能額外的擴展方法

inherit(MouseInput, Input, {
     handler: function MEhandler(ev){
    }
}

子類MouseInput繼承父類Input類的所有原型方法,並擴展了handler方法

 


輸入設備初始化繼承:就是通過什么設備觸發動作(PC,手機,ipad等等)

輸入設備hammer分為

MouseInput,PointerEventInput,SingleTouchInput,TouchInput,TouchMouseInput

我們看看最簡單的桌面PC的鼠標輸入處理MouseInput,其余的結構基本類似。

function MouseInput() {
    this.evEl = MOUSE_ELEMENT_EVENTS;
    this.evWin = MOUSE_WINDOW_EVENTS;
    //用來禁止TouchMouse事件
    this.allow = true; // used by Input.TouchMouse to disable mouse events
    //鼠標按下的狀態
    this.pressed = false; // mousedown state
    Input.apply(this, arguments);
}

inherit(MouseInput, Input, {
     handler: function MEhandler(ev){
       ..//////////////
    }
}

MouseInput初始化了幾個必要的判斷屬性,然后就只handler方法, 此外還集成了Input輸入類

比如我們調用

new MouseInput(callback)的時候,通過Input.apply(this, arguments)去初始化了基類input類,然后基類內部的init綁定了事件,並且把事件的回調,

this.domHandler指向了外部的callback回調,其實也就是handler方法了

addEventListeners(this.element, this.evEl, this.domHandler);

 

另一個基類就是Recognizer

因為我們把用戶的行為分為單擊tap , 雙擊doubletap,平移pan,滑動swipe,長按press,縮放pinch,旋轉rotate,那么類似相同點我們也必須抽象成一個基類

Recognizer比如復雜,留在執行期的時候講解。

 


Hammer 使用:

var mc = new Hammer(el);

那么內部的構建

function Hammer(element, options) {
    options = options || {};
    //配置手勢識別器參數
    options.recognizers = ifUndefined(options.recognizers, Hammer.defaults.preset);
    return new Manager(element, options);
}

可見最終是Manager構建的對象實例了

Manager內部,通過createInputInstance創建一個輸入環境的實例對象,創建一個輸入環境的實例對象

this.input = createInputInstance(this);

createInputInstance的作用主要是用來選擇當然的平台,不同的平台會調用不同的手勢輸入處理,這里就有策略選擇了的處理了

function createInputInstance(manager) {
    var Type;
    var inputClass = manager.options.inputClass;

    if (inputClass) {
        Type = inputClass;
    } else if (SUPPORT_POINTER_EVENTS) {
        Type = PointerEventInput;
    } else if (SUPPORT_ONLY_TOUCH) {
        Type = TouchInput; //移動手機端
    } else if (!SUPPORT_TOUCH) {
        Type = MouseInput; //桌面
    } else {
        Type = TouchMouseInput;
    }
}

如果是桌面PC端,我們就會走MouseInput

return new MouseInput(manager, inputHandler);

這樣把具體的通過Input類綁定的回調放到MouseInput的handler處理了,最終的回調會進入總處理inputHandler類

inputHandler類就會遍歷所有的手勢識別器把輸入的input傳入

manager.recognize(input);

每一個識別器各自處理其行為了,當然這里面倒是如何觸發,手勢識別器如何判斷是那種手勢,就放一章了。

 

官方:http://hammerjs.github.io/

我的:https://github.com/JsAaron/hammer-js

 


免責聲明!

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



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