1. 程式人生 > >hammer.js移動端開發手勢庫

hammer.js移動端開發手勢庫

原文轉載自https://www.cnblogs.com/vajoy/p/4011723.html

hammerJS是一個優秀的、輕量級的觸屏裝置手勢庫,現在已經更新到2.04版本,跟1.0版本有點天壤地別了,畢竟改寫了事件名並新增了許多方法,允許同時監聽多個手勢、自定義識別器,也可以識別滑動方向。

不過對於新版本的hammerJS卻及其匱乏中文指引文件,就著這一點我還是上官網翻譯下英文文件,寫一篇跟大家分享吧(其實hammer的API寫的很不怎樣,內容和排版都很馬虎了事,建議先仔細研究examples後再查閱。你也可以通過Aaron豬肉榮的Hammer系列文章來學習)

注:本文將所有API中提到的 “input” 翻譯為 “互動”,它實際包括mousedown, mousemove, touchmove, pointercancel事件。

GENERAL

開始

HammerJS是一個開源的庫,可以識別由 touch, mouse 和 pointerEvents 觸發的系列手勢。它非常小巧,壓縮後僅有3.96kb,並沒有多餘的指令碼依賴。

你可以從Github上獲取最新版的HammerJS,或者直接下載壓縮版 或 未壓縮的開發版的HammerJS原始碼。

點此獲取版本變動日誌。

也可以點這裡獲取更舊的1.1版本。

2.0版本的變動:徹底重寫了原始碼,包括可複用的識別器(recognizer)、提升了對最新移動端瀏覽器可用的觸控行為css屬性的支援,另支援了多個hammer例項同時使用,讓多使用者同時使用一臺裝置也不在話下。

使用

HammerJS的使用方式非常簡單,只要將庫引入到檔案中,並建立一個新的例項即可:

var hammertime = new Hammer(myElement, myOptions);
hammertime.on('pan', function(ev) {
    console.log(ev);
});

它會預設為這個物件新增一系列識別器,包括 tap<點>, doubletap<雙點選>, press<按住>, 水平方位的pan<平移> 和 swipe<快速滑動>, 以及多觸點的 pinch<捏放> 和 rotate<旋轉>識別器。不過呢,其中的 pinch 和 rotate 預設是不可用的,因為它們可能會導致元素被卡住,如果你想啟用它們,可以加上這兩句:

hammertime.get('pinch').set({ enable: true });
hammertime.get('rotate').set({ enable: true });

若要允許識別器識別垂直方位或全部方位的 pan 和 swipe,可以這麼寫:

hammertime.get('pan').set({ direction: Hammer.DIRECTION_ALL });
hammertime.get('swipe').set({ direction: Hammer.DIRECTION_VERTICAL });

另建議加上如下meta標籤,防止doubletap 或 pinch 縮放了viewport:

<meta name="viewport" content="user-scalable=no, width=device-width, initial-scale=1, maximum-scale=1">

更多控制

你可以為你的例項設定屬於你自己的識別器,雖然要多寫一點程式碼,但能讓你控制更多能被識別的手勢:

複製程式碼
var mc = new Hammer.Manager(myElement, myOptions);

mc.add( new Hammer.Pan({ direction: Hammer.DIRECTION_ALL, threshold: 0 }) );
mc.add( new Hammer.Tap({ event: 'quadrupletap', taps: 4 }) );

mc.on("pan", handlePan);
mc.on("quadrupletap", handleTaps);
複製程式碼

上述的程式碼建立了一個例項(mc),它包含了一個 pan 和一個 quadrupletap 手勢,識別器例項會在它們被新增(add)之後就不斷地執行,且(一個識別器例項)只能識別一個(手勢)。

貼士和竅門

1. 試著避免垂直方向上的 pan/swipe

垂直方向上的平移操作一般是用來滾動你的頁面的,而且有些(過時的)瀏覽器不會傳遞事件,導致Hammer無法識別這些手勢。你可以嘗試換另一種可替換的途徑來實現相同的動作。

2. 在裝置上做測試

有時候Hammer需要做一些調整,像swipe的速率或其它閾值,如果你在一臺反應較慢的裝置上做測試,那麼你要保證你的回撥越簡單越好。有些站點例如JankFree.org上有專門的文章來介紹如何提升展示效果。

3. 去掉Windows Phone點選時的高亮效果

你在Windows Phone上的IE10和IE11裡tap某元素時,會有一個小小的tap高亮效果,加上這個meta標籤可以取消這種效果:

<meta name="msapplication-tap-highlight" content="no" />

4. "我怎麼選擇不了文字了!"

Hammer設定了一個屬性用來提升桌面平移操作的使用者體驗(UX)。常規來說,當你在桌面級瀏覽器上拖動頁面時,你應該是可以正常選中文字的,但user-select這個CSS屬性禁用了這功能。
如果你在意文字選擇功能,同時覺得桌面級的體驗沒必要太盡善盡美,你可以很輕鬆地取消這個預設選項——確保在建立例項之前執行:

delete Hammer.defaults.cssProps.userSelect;

5. “在tap之後,導致觸發了一個click事件,我不想這樣!”
這種click事件我們稱之為一個“幽靈點選”事件,我建立了一個小函式來避免觸控後導致click,對此,Ryan Fioravanti的文章給了我很大的靈感。

瀏覽器/終端的支援

無須擔心你的瀏覽器或系統不在下方的列表上,Harmmer可以執行在除了IE8-的任何地方。瀏覽器若對觸控行為(touch-action)提供原生支援,那麼對比那些不支援的瀏覽器,會有更好的體驗。檢視touch-action頁面瞭解更多。

例項

HAMMER

常規API

Utils

==============================

Hammer

建立並返回一個帶有系列預設識別器集合的Manager例項,該集合內包含了諸如 tap, doubletap, pan, swipe, press, pinch 和 rotate 識別器。你應該在初始化時執行它,其語法為:

Contructor(HTMLElement, [options])

引數裡一個是你的頁面元素,另一個是可選的識別器選項options,options會融入Hammer.defaults中去,當然,在Hammer.defaults.preset中定義的識別器集合也會被新增進來。

如果識別器選項options為空,那麼初始化的時候不會有額外的識別器被新增進來:

var myElement = document.getElementById('hitarea');
var mc = new Hammer(myElement);

==============================

Hammer.defaults

建立例項時初始化的預設值,包括你定義的options選擇器項。其屬性包括:

touchAction: 'compute'

其值可為 compute, auto, pan-y, pan-x 或 none 。預設選項會基於識別器為你選擇一個正確值。

domEvents: false
讓Hammer也能禁用DOM事件。若不禁用會有些慢,故預設是禁用的。如果你想實現事件委託,那麼建議你將其設為true。

enable: true
接受一個boolean值, 或返回一個boolean值的函式。(官網就這樣一句話,也沒說具體啥作用,汗~)

cssProps: {....}
可以改善互動事件操作的系列css屬性。更多詳情可以查閱JSDoc

preset: [....]
呼叫Hammer()的時候就安裝了預設的識別器。如果建立一個新的Manager,這些將被跳過。

==============================

Hammer.Manager

Manager是所有識別器例項的容器,它為你的元素安裝了互動事件監聽器,並設定了觸控事件特性。

constructor(HTMLElement, [options])

引數為你的元素(HTMLElement)和選項(options),選項將合併到Hammer.defaults中去:

var mc = new Hammer.Manager(myElement);

你可以在選項中使用 recognizers 來設定一個初始化識別器,它是一個數組,寫法如下:

複製程式碼
var mc = new Hammer.Manager(myElement, {
    recognizers: [
        // RecognizerClass, [options], [recognizeWith, ...], [requireFailure, ...]
        [Hammer.Rotate],
        [Hammer.Pinch, { enable: false }, ['rotate']],
        [Hammer.Swipe,{ direction: Hammer.DIRECTION_HORIZONTAL }],
    ]
});
複製程式碼

set(options)

修改一個Manager例項的選項,該方法是推薦使用的,它可以在需要的時候更新touchAction的值:

mc.set({ enable: true });

get(string), add(Recognizer) 和 remove(Recognizer)

新增一個新的Recognizer例項到Manager中,新增的順序跟識別器執行的順序一致。get方法會返回被新增的Recognizer例項。
get和remove方法都把一個(識別器中的)事件名或識別器例項來作為一個引數。
Add 和 remove 方法也接受一個識別器陣列來作為引數:

// both return instance of myPinchRecognizer
mc.get('pinch');
mc.get(myPinchRecognizer);
mc.add(myPinchRecognizer); // returns the recognizer
mc.add([mySecondRecogizner, myThirdRecognizer]);
mc.remove(myPinchRecognizer);
mc.remove('rotate');
mc.remove([myPinchRecognizer, 'rotate']);

on(events, handler) 和 .off(events, [handler])

監聽由被新增的識別器觸發的事件,或者移除那些綁定了的事件。引數中將事件通過空格隔開可處理多個事件:

mc.on("pinch", function(ev) {
    console.log(ev.scale);
});

stop([force])

停止當前互動會話的識別器(Stop recognizing for the current input session)。當使用force引數時,將強制立刻停止識別器執行週期。

destroy()

解綁所有互動事件並讓manager失去作用,但它沒有解綁任何dom事件監聽器。

==============================

Hammer.Recognizer

每一個識別器都是從這個類中擴展出來的,所有識別器都會有一個enable選項,其值為boolean或者一個回撥函式,用來啟用/禁用非底層的識別器。

constructor([options])

只有選項作為引數:

var pinch = new Hammer.Pinch(); //建立一個識別器
mc.add(pinch); // 新增到Manager例項中

set(options)

在識別器例項中修改一個選項。該方法是推薦使用的,它可以在需要的時候更新touchAction的值。

recognizeWith(otherRecognizer) 和 dropRecognizeWith(otherRecognizer)

在當前識別器執行的時候同步執行所給的其它識別器(otherRecognizer),當你需要在最後結合pan和swipe手勢時,或者需要同時pinch和ratate一個物件時,它會很有幫助。
移除這種聯絡時,只會移除當前識別器上的連線,而不是其它識別器(otherRecognizer)上的連線。
這兩個方法都支援一個識別器組成的陣列來作為引數。
如果識別器被新增到了Manager上,那麼該方法也支援將其它識別器(otherRecognizer)的事件名(字串形式)來作為引數。

requireFailure(otherRecognizer) 和 dropRequireFailure(otherRecognizer)

只有當其它識別器(otherRecognizer)無效時才執行該識別器。
移除這種聯絡時,只會移除當前識別器上的連線,而不是其它識別器(otherRecognizer)上的連線。
這兩個方法都支援一個識別器組成的陣列來作為引數。
如果識別器被新增到了Manager上,那麼該方法也支援將其它識別器(otherRecognizer)的事件名(字串形式)來作為引數。

==============================

Hammer.input 事件

hammer.input可以觸發一個“祕密的”事件,它發生在每一個接收中的互動中,也讓你能對原生的互動來做相關處理。它是一個小而強大的特性。

hammertime.on("hammer.input", function(ev) {
   console.log(ev.pointers);
});

==============================

事件物件

每一個Hammer觸發的事件都會收到一個包含了如下屬性的事件物件:

==============================

常量/Constants(這個建議查閱原始碼338行起,主要是用於標誌事件輪廓,可通過上文“事件物件”的direction、offsetDirection等屬性來獲取)

所有常量都定義於Hammer物件中,因為它們都是二進位制標識,你可以使用位運算來操作它們。MDN上有一些關於位運算的優秀文件

方位/Directions

用於定義一個識別器的方位,並用來讀取一個事件的對應值。

互動事件/Input Events

Hammer匹配所有互動 (mousedown, mousemove, touchmove, pointercancel)事件型別為如下值:

識別器狀態/Recognizer States

由識別器在內部定義自己的狀態:

==============================

工具/Utils

Hammer.on(element, types, handler)

addEventListener的封裝,可以接受多個事件型別為引數:

Hammer.on(window, "load resize scroll", function(ev) {
    console.log(ev.type);
});

Hammer.off(element, types, handler)

如同Hammer.on,是removeEventListener的封裝,也允許多個事件型別為引數。

Hammer.each(obj, handler)

遍歷一個數組或物件:

Hammer.each([10,20,30,40], function(item, index, src) { });
Hammer.each({a:10, b:20, c:30}, function(item, key, src) { });

Hammer.merge(obj1, obj2)

把obj2的屬性混到obj1中去,不過obj1的已有屬性不會被重寫:

複製程式碼
var options = {
    b: false
};

var defaults = {
    a: true,
    b: true,
    c: [1,2,3]
};
Hammer.merge(options, defaults);

// options.a == true
// options.b == false
// options.c == [1,2,3]
複製程式碼

Hammer.extend(obj1, obj2)

把obj2的屬性擴充套件到obj1中去,不過obj1的已有屬性會被重寫:

複製程式碼
var obj1 = {
    a: true,
    b: false,
    c: [1,2,3]
};

var obj2 = {
    b: true,
    c: [4,5,6]
};
Hammer.extend(obj1, obj2);

// obj1.a == true
// obj1.b == true
// obj1.c == [4,5,6]
複製程式碼

Hammer.inherit(child, base, [properties])

簡單的類繼承:

複製程式碼
function Animal(name) {
    this.name = name;
}

function Dog() {
    Animal.apply(this, arguments);
}

Hammer.inherit(Dog, Animal, {
    bark: function() {
        alert(this.name);
    }
});

var dog = new Dog('Spaikie');
dog.bark();
複製程式碼

Hammer.bindFn(fn, scope)

Function.bind的簡化形式:

複製程式碼
function myFunction(ev) {
    console.log(this === myContext); // is true
}

var myContext = {
    a: true,
    b: false
};

window.addEventListener('load', Hammer.bindFn(myFunction, myContext), false);
複製程式碼

Hammer.prefixed(obj, name)

獲取瀏覽器的(字首)屬性值:

Hammer.prefixed(document.body.style, 'userSelect');
// returns "webkitUserSelect" on Chrome 35

關於hammer的API就翻譯到這裡,剩餘的幾個頁面內容篇幅較少也較好讀懂,請自行查閱和理解。

這篇文章是10月8日提筆的,後續因為換工作、換城市的事宜被擱置、塵封在草稿箱,直到今天突然想起才決定把它寫完,供打算入門hammer的朋友做個參考吧(再次吐槽hammer的api寫的很草率)。

共勉~