想不到又到週末了,週末的時間要抓緊學習才行,前幾天咱們學習了iScroll幾點基礎知識:javascript
今天咱們來學習其事件機制以及滾動條的實現,完了後咱們iScroll就學習的差很少了,最後會抽離iScroll的精華部分組成一個閹割版iScrollhtml
並總結下iScroll的一些地方結束iScroll的學習,而後完全撲向nodeJS了前端
咱們平時所說的事件機制其實應該分開,分紅兩塊:java
① DOM的事件相關node
② 系統自建事件機制ios
在咱們前端的頁面裏面,最重要的固然是交互,交互其實就是一個個事件的體現,因此任何前端庫的核心必定是其事件,iScroll就是由三大事件串聯整個流程web
iScroll一樣包括DOM事件以及自建事件,其中DOM事件即是瀏覽器的表現,而自建事件就是用戶能夠插一腳的地方了express
iScroll DOM事件實現與可能讓一些不熟悉javascript事件機制的同窗大跌眼鏡(在與Aaron討論前,我其實也摸不着頭腦)數組
簡單來講,標準狀況下咱們這樣實現事件註冊瀏覽器
el.addEventListener(type, fn, capture)
其中的全部參數都沒有問題,惟獨第二個參數,爲何這麼說呢?請看如下代碼:
1 var eventObj = {}; 2 eventObj.a = 1; 3 document.addEventListener('click', eventObj)
各位以爲這個代碼有問題嗎?第二個參數顯然不是一個函數,可是function也是object呢,其實這樣也是javascript規範之一,不知道只是咱們寡聞而已
這樣寫有如下好處,咱們的做用域就是咱們的對象:
var eventObj = {}; eventObj.a = 1; eventObj.handleEvent = function () { alert(this.a); } document.addEventListener('click', eventObj) //這個代碼點擊會彈出1
這個即是一個javascript規範,咱們傳入的對象若是具備handleEvent 函數,便會執行,若是沒有,這次註冊便無心義,這樣綁定的話,做用域便指向了eventObj
有了以上知識,再說回iScroll的DOM事件:
① 構造函數會執行_initEvents方法初始化事件,咱們抽出咱們關心的一塊:
1 eventType(this.wrapper, 'touchstart', this); 2 eventType(target, 'touchmove', this); 3 eventType(target, 'touchcancel', this); 4 eventType(target, 'touchend', this);
var eventType = remove ? utils.removeEvent : utils.addEvent
這個代碼其實就是調用的addEvent方法:
1 me.addEvent = function (el, type, fn, capture) { 2 el.addEventListener(type, fn, !!capture); 3 };
那麼iScroll事件綁定的具體點便捕捉到了:
能夠看到咱們這裏的fn是一個對象,可是不要擔憂,咱們的具體的方法在此:
1 handleEvent: function (e) { 2 switch (e.type) { 3 case 'touchstart': 4 case 'MSPointerDown': 5 case 'mousedown': 6 this._start(e); 7 break; 8 case 'touchmove': 9 case 'MSPointerMove': 10 case 'mousemove': 11 this._move(e); 12 break; 13 case 'touchend': 14 case 'MSPointerUp': 15 case 'mouseup': 16 case 'touchcancel': 17 case 'MSPointerCancel': 18 case 'mousecancel': 19 this._end(e); 20 break; 21 case 'orientationchange': 22 case 'resize': 23 this._resize(); 24 break; 25 case 'transitionend': 26 case 'webkitTransitionEnd': 27 case 'oTransitionEnd': 28 case 'MSTransitionEnd': 29 this._transitionEnd(e); 30 break; 31 case 'wheel': 32 case 'DOMMouseScroll': 33 case 'mousewheel': 34 this._wheel(e); 35 break; 36 case 'keydown': 37 this._key(e); 38 break; 39 case 'click': 40 if (!e._constructed) { 41 e.preventDefault(); 42 e.stopPropagation(); 43 } 44 break; 45 } 46 }
高大帥哈,如此整個iScroll的DOM事件相關就沒問題了,在具體就回到了上次的三大事件點了
其實在咱們學習backbone時候咱們就提到了這塊操做
1 var Events = Backbone.Events = { 2 3 on: function (name, callback, context) { 4 if (!eventsApi(this, 'on', name, [callback, context]) || !callback) return this; 5 this._events || (this._events = {}); 6 var events = this._events[name] || (this._events[name] = []); 7 events.push({ callback: callback, context: context, ctx: context || this }); 8 return this; 9 }, 10 11 off: function (name, callback, context) { 12 var retain, ev, events, names, i, l, j, k; 13 if (!this._events || !eventsApi(this, 'off', name, [callback, context])) return this; 14 if (!name && !callback && !context) { 15 this._events = {}; 16 return this; 17 } 18 19 names = name ? [name] : _.keys(this._events); 20 for (i = 0, l = names.length; i < l; i++) { 21 name = names[i]; 22 if (events = this._events[name]) { 23 this._events[name] = retain = []; 24 if (callback || context) { 25 for (j = 0, k = events.length; j < k; j++) { 26 ev = events[j]; 27 if ((callback && callback !== ev.callback && callback !== ev.callback._callback) || 28 (context && context !== ev.context)) { 29 retain.push(ev); 30 } 31 } 32 } 33 if (!retain.length) delete this._events[name]; 34 } 35 } 36 37 return this; 38 }, 39 40 trigger: function (name) { 41 if (!this._events) return this; 42 var args = slice.call(arguments, 1); 43 if (!eventsApi(this, 'trigger', name, args)) return this; 44 var events = this._events[name]; 45 var allEvents = this._events.all; 46 if (events) triggerEvents(events, args); 47 if (allEvents) triggerEvents(allEvents, arguments); 48 return this; 49 }, 50 }; 51 52 // Regular expression used to split event strings. 53 var eventSplitter = /\s+/; 54 55 // Implement fancy features of the Events API such as multiple event 56 // names `"change blur"` and jQuery-style event maps `{change: action}` 57 // in terms of the existing API. 58 var eventsApi = function (obj, action, name, rest) { 59 if (!name) return true; 60 61 // Handle event maps. 62 if (typeof name === 'object') { 63 for (var key in name) { 64 obj[action].apply(obj, [key, name[key]].concat(rest)); 65 } 66 return false; 67 } 68 69 // Handle space separated event names. 70 if (eventSplitter.test(name)) { 71 var names = name.split(eventSplitter); 72 for (var i = 0, l = names.length; i < l; i++) { 73 obj[action].apply(obj, [names[i]].concat(rest)); 74 } 75 return false; 76 } 77 78 return true; 79 };
所謂自建事件機制,實際上是唬人的,就是用一個數組保存各個階段的函數,到特定階段執行即可,iScroll這塊作的尤爲簡單,並且又註冊沒有註銷:
1 on: function (type, fn) { 2 if (!this._events[type]) { 3 this._events[type] = []; 4 } 5 6 this._events[type].push(fn); 7 }, 8 9 _execEvent: function (type) { 10 if (!this._events[type]) { 11 return; 12 } 13 14 var i = 0, 15 l = this._events[type].length; 16 17 if (!l) { 18 return; 19 } 20 21 for (; i < l; i++) { 22 this._events[type][i].call(this); 23 } 24 },
iScroll在構造函數中定義了_events這一對象,而後即可以開開心心使用on註冊各類各樣的事件了,其中每種事件對象是一個數組
定義好好,在特定階段,好比touchstart階段,便開開有沒有註冊相關事件,註冊了便執行一發便可:
this._execEvent('scrollEnd');
這裏要注意的是,他的this執行爲iScroll,那麼就可使用不少有用的屬性了
至此,iScroll事件機制一塊咱們便分析結束了,接下來來簡單看看咱們關心的滾動條的實現
這裏須要注意的一點是,這種實現的好處其實一個是方便在各個階段註冊、觸發相關事件,主要原因仍是便於放出接口給外部調用
其實到這裏,咱們隊iScroll的解析都七七八八了,這裏我不得不說,iScroll雖然動畫感覺作的好之外,仍是可能致使一些問題
iScroll自己沒什麼問題,問題出在各類各樣的瀏覽器中,據我讀代碼的感覺以及平時工做中遇到的問題,我相信項目中使用iScroll的朋友有可能
固然,這些問題出如今手機中:
① 當滑動碰到input可能出現滑動不順的問題
② 滑動時候具備input時候滑動順暢的話,input獲取焦點不易
③ 點擊時候可能出現問題(可能不能點擊,可能雙次點擊)
④ 當你在ios點擊時候碰到alert相似的東西,再點其它地方事件可能會重複觸發
⑤ ......
固然以上問題只是個人猜想,是否真會致使問題還得通過驗證,請各位不要搭理我,若是真有相似問題,獲取其它問題請留言
上面扯了那麼多也沒有什麼意義,咱們如今仍是來看滾動條的實現吧:
iScroll爲滾動條單獨搞了一個類出來,由於在iScroll裏面的滾動條是一等公民,具備如下特性:
① 鼠標中間滾動
② 可拖動滾動條
其實,多數時間以上功能能夠取締,尤爲在手機上,其可點擊區域仍是太小,單獨用於手機的話,鼠標中間也無心義
PS:iscroll使用鍵盤上下鍵也能夠滾動,真的是大而全的功能啊,可是無心義......至少在移動端意義不大,去掉還能夠節約1k的流量
1 function Indicator(scroller, options) { 2 this.wrapper = typeof options.el == 'string' ? document.querySelector(options.el) : options.el; 3 this.wrapperStyle = this.wrapper.style; 4 this.indicator = this.wrapper.children[0]; 5 this.indicatorStyle = this.indicator.style; 6 this.scroller = scroller; 7 8 this.options = { 9 listenX: true, 10 listenY: true, 11 interactive: false, 12 resize: true, 13 defaultScrollbars: false, 14 shrink: false, 15 fade: false, 16 speedRatioX: 0, 17 speedRatioY: 0 18 }; 19 20 for (var i in options) { 21 this.options[i] = options[i]; 22 } 23 24 this.sizeRatioX = 1; 25 this.sizeRatioY = 1; 26 this.maxPosX = 0; 27 this.maxPosY = 0; 28 29 if (this.options.interactive) { 30 if (!this.options.disableTouch) { 31 utils.addEvent(this.indicator, 'touchstart', this); 32 utils.addEvent(window, 'touchend', this); 33 } 34 if (!this.options.disablePointer) { 35 utils.addEvent(this.indicator, 'MSPointerDown', this); 36 utils.addEvent(window, 'MSPointerUp', this); 37 } 38 if (!this.options.disableMouse) { 39 utils.addEvent(this.indicator, 'mousedown', this); 40 utils.addEvent(window, 'mouseup', this); 41 } 42 } 43 44 if (this.options.fade) { 45 this.wrapperStyle[utils.style.transform] = this.scroller.translateZ; 46 this.wrapperStyle[utils.style.transitionDuration] = utils.isBadAndroid ? '0.001s' : '0ms'; 47 this.wrapperStyle.opacity = '0'; 48 } 49 }
設個就是滾動條的構造函數,這有一個關鍵點:
interactiveScrollbars: true
默認咱們的滾動條是不會具備滾動等事件的,若是設置了此參數便具備可拖動特性了
1 if (this.options.interactive) { 2 if (!this.options.disableTouch) { 3 utils.addEvent(this.indicator, 'touchstart', this); 4 utils.addEvent(window, 'touchend', this); 5 } 6 if (!this.options.disablePointer) { 7 utils.addEvent(this.indicator, 'MSPointerDown', this); 8 utils.addEvent(window, 'MSPointerUp', this); 9 } 10 if (!this.options.disableMouse) { 11 utils.addEvent(this.indicator, 'mousedown', this); 12 utils.addEvent(window, 'mouseup', this); 13 } 14 }
這裏雖然給滾動條綁定的事件,可是會一併操做咱們的body主體,可是咱們後面會直接忽略這步操做
1 if (this.options.fade) { 2 this.wrapperStyle[utils.style.transform] = this.scroller.translateZ; 3 this.wrapperStyle[utils.style.transitionDuration] = utils.isBadAndroid ? '0.001s' : '0ms'; 4 this.wrapperStyle.opacity = '0'; 5 }
而後,會給滾動條一個漸隱的效果,這個影響較小,直接使用了CSS3實現
下面繼續實現了他的事件handleEvent
接下來又是touch幾個事件了:
這個地方因爲咱們後面不會實現便直接不予關注了
忽然來了幾個BUG,等下要發佈測試環境了,今天暫時到這裏,咱們下次繼續好了,下次咱們就直接分離iScroll了,抽出咱們想要的功能