AlloyFinger 是由騰訊前端團隊 AlloyTeam 出品的一個小巧輕量級的移動端手勢庫,整個手勢庫的代碼不超過400行,卻支持絕大多數的手勢操做,可以知足平常的開發需求。AlloyFinger傳送門: AlloyFinger。javascript
手機移動端瀏覽器提供了4種觸摸事件:touchstart,touchmove,touchend,touchcancel,分別對應的是手指觸點剛接觸屏幕時觸發事件,手指觸點在屏幕上移動時觸發事件,手指觸點移開屏幕時觸發事件以及被系統中斷時觸發事件(按 Home 鍵返回主屏等操做)。前端
這裏要說明下,移動端瀏覽器也支持部分 PC 端帶有的事件,好比 click 事件。可是在移動端上,click 事件會存在延時觸發的狀況,大概延時300ms。java
在移動端爲何click事件會存在延時觸發的狀況呢?究其緣由,是由於蘋果公司在早期發佈iphone的時候,採用了雙擊縮放網頁的設計。當用戶手指點擊一次屏幕時,瀏覽器不能當即斷定用戶操做是單擊操做仍是雙擊操做,而是延遲了300ms,以判斷用戶是否再次點擊了屏幕,若是300ms以內沒有再次點擊屏幕就斷定爲單擊事件,纔會去觸發click事件。git
AlloyTeam 團隊爲 AlloyFinger 打造了多個可以適用不一樣技術棧中的手勢庫版本,可以方便的使用在 React 框架,Vue框架以及原生JS中。不一樣場景下的手勢庫版本的實現思路都是同樣的,因此這裏只分析了原生JS的實現思路。github
AlloyFinger 的使用方式很是簡單,源碼中暴露出了一個全局的 AlloyFinger 構造函數對象,使用方式以下,返回值是一個 AlloyFinger 實例對象。web
// element 是須要手勢操做的DOM元素,值能夠是DOM對象也能夠是元素選擇器。
// options 是一個對象,包含了須要的手勢操做函數。
var af = new AlloyFinger(element, options);
var af = new AlloyFinger(element, {
tap: function() {
//do something...
}
});
複製代碼
有了 AlloyFinger 實例對象後,你還能夠經過綁定自定義事件的方式使用手勢庫瀏覽器
//綁定手勢事件
af.on('tap', function() {
//do something...
});
//解綁手勢事件
af.off('tap', function() {
//do something...
});
//銷燬實例
af.destroy();
複製代碼
AlloyFinger 構造函數bash
首先,先定義了一個 AlloyFinger 構造函數,裏面作了不少操做,事件的監聽回調,變量值的初始化,將手勢操做做爲訂閱者添加到訂閱列表中。在這部分源碼中,會初始化不少關於手指觸點的水平座標和垂直座標的存儲變量,剛開始看的時候會以爲代碼比較的混亂,因此筆者把這部分的變量捋一遍梳理了出來,便於清晰的閱讀源碼。架構
this.x1: 存儲在剛開始觸摸時第一個手指觸點的X座標位置
this.y1: 存儲在剛開始觸摸時第一個手指觸點的Y座標位置
this.preV.x: 存儲第一個手指觸點與第二個手指觸點之間的水平間距
this.preV.y: 存儲第一個手指觸點與第二個手指觸點之間的垂直間距
this.x2: 存儲在移動操做時第一個手指觸點的X座標位置
this.y2: 存儲在移動操做時第一個手指觸點的Y座標位置
this.sx2: 存儲在移動操做時第二個手指觸點的X座標位置
this.sy2: 存儲在移動操做時第二個手指觸點的Y座標位置
複製代碼
整個的源碼解讀都放置在個人github上,幾乎每一行都有本身的註解,感興趣的話能夠點擊這裏:傳送門。框架
源碼都是精簡幹練的,多看優秀的源碼仍是對本身的技術有幫助的,可能看完了以後會思考本身怎麼去DIY一個手勢庫呢?想要本身怎麼去DIY一個手勢庫,必須得先了解各個手勢操做的實現思路,有思路了以後才能動手寫代碼。
tap的本質其實就是touchend,可是在具體實現的時候必須作下限制,當前只存在一個手指觸點,且touchstart的時候手指觸點和touchend時手指觸點的X軸Y軸的誤差不能小於30,這樣才能斷定當前的操做是tap操做。
var len = evt.touches.length;
if(len < 1) {
if ((this.x2 && Math.abs(this.x1 - this.x2) <= 30) ||
(this.y2 && Math.abs(this.y1 - this.y2) <= 30)) {
//我是tap操做,do something...
}
}
複製代碼
doubleTap雙擊操做的實現思路大體是這樣的,得先判斷一段時間內是否有兩次touchstart操做,而且兩次touchstart都是快速完成的,否則會被認爲是長按操做了,還有一點就是兩次觸點的位置的X軸Y軸的誤差不能小於30。
//存儲手指按下觸摸操做的時間戳
this.now = null;
//存儲上一次手指觸點觸摸的時間戳
this.last = null;
//用於存儲手指觸摸操做時的水平座標和垂直座標(若是是多指觸摸操做,則記錄的是第一個手指觸摸的位置)
this.preTapPosition = { x: null, y: null };
//是否爲雙擊操做
this.isDoubleTap = false;
...
function start() {
this.now = Date.now();
if (this.preTapPosition.x !== null) {
//若是手指連續觸摸操做之間的時間間隔小於250毫秒,且手指連續觸摸操做之間的觸點位置水平座標小於30,垂直座標小於30,那麼就斷定該操做爲雙擊操做
this.isDoubleTap = (this.delta > 0 && this.delta <= 250 && Math.abs(this.preTapPosition.x - this.x1) < 30 && Math.abs(this.preTapPosition.y - this.y1) < 30);
}
this.preTapPosition.x = this.x1;
this.preTapPosition.y = this.y1;
this.last = this.now;
}
function end() {
if (this.isDoubleTap) {
//我是doubleTap操做,do something...
}
}
複製代碼
swipe滑過操做具體的實現思路是touchstart的手指觸點的座標和touchend時候手指觸點的座標x、y方向偏移要大於30,且還要判斷是往哪一個方向滑動。
//斷定swipe滑動的方向
_swipeDirection: function (x1, x2, y1, y2) {
return Math.abs(x1 - x2) >= Math.abs(y1 - y2) ? (x1 - x2 > 0 ? 'Left' : 'Right') : (y1 - y2 > 0 ? 'Up' : 'Down')
}
複製代碼
更多的手勢操做源碼分析能夠參考個人github上的源碼分析,傳送門。AlloyFinger 手勢庫還用在了一個小巧的移動端裁剪圖片工具上,下次還能夠分析一波裁剪工具 AlloyCrop 的源碼,學習到裁剪圖片的原理和實現方案,平時在開發過程當中,其實只要清楚了實現思路和原理,就可以方便的實現具體的功能。