前言
最近兩天看到很多的總結性發言,我想想今年好像我的變化挺大的,是不是該晚上來水一發呢?嗯,決定了,晚上來水一發!
上周六,我們簡單模擬了下iScroll的實現,周日我們開始了學習iScroll的源碼,今天我們接着上次的記錄學習,因為最近事情稍微有點多了
學習進度可能要放慢,而且iScroll這個庫實際意義很大,不能囫圇吞棗的學習,要學到精華,並且要用於項目中的,所以初步規划是最近兩周主要圍繞iScroll展開
而后兩個選擇:① 分離iScroll代碼用於項目;② 抄襲iScroll精華部分用於項目。無論如何都要用於項目......
幾個事件點
iScroll的整體邏輯由三大事件點組成:
① touchStart(mousedown)
② touchMove(mousemove)
③ touchEnd(mouseUp)
也就是iScroll整體的功能邏輯其實是由這幾個事件串起來的,其中
touchStart會保留一些初始化操作,或者停止正在進行的動畫
touchMove會帶動dom一起移動
而touchEnd最為復雜,在touchend階段可能需要處理很多東西
① 一般性拖動結束事件
② 超出邊界還原后觸發的事件(此時可以滾動加載數據)
③ 如果此次為一次按鈕點擊,需要觸發按鈕事件那么還有對preventDefault進行處理(preventDefault可能導致事件不觸發)
④ 如果此次為一次點擊事件,並且對象為文本框或者select(其它會獲得焦點的事件),那么應該讓其獲得焦點,並且彈出鍵盤
⑤ ......
以上為主觀臆測下的猜想,我們來看看iScroll實際干了些什么,下面再細細的分析各個階段
start
1 _start: function (e) { 2 // React to left mouse button only 3 if (utils.eventType[e.type] != 1) { 4 if (e.button !== 0) { 5 return; 6 } 7 } 8 if (!this.enabled || (this.initiated && utils.eventType[e.type] !== this.initiated)) { 9 return; 10 } 11 if (this.options.preventDefault && !utils.isBadAndroid && !utils.preventDefaultException(e.target, this.options.preventDefaultException)) { 12 e.preventDefault(); 13 } 14 var point = e.touches ? e.touches[0] : e, 15 pos; 16 17 this.initiated = utils.eventType[e.type]; 18 this.moved = false; 19 this.distX = 0; 20 this.distY = 0; 21 this.directionX = 0; 22 this.directionY = 0; 23 this.directionLocked = 0; 24 25 this._transitionTime(); 26 27 this.startTime = utils.getTime(); 28 29 if (this.options.useTransition && this.isInTransition) { 30 this.isInTransition = false; 31 pos = this.getComputedPosition(); 32 this._translate(Math.round(pos.x), Math.round(pos.y)); 33 this._execEvent('scrollEnd'); 34 } else if (!this.options.useTransition && this.isAnimating) { 35 this.isAnimating = false; 36 this._execEvent('scrollEnd'); 37 } 38 39 this.startX = this.x; 40 this.startY = this.y; 41 this.absStartX = this.x; 42 this.absStartY = this.y; 43 this.pointX = point.pageX; 44 this.pointY = point.pageY; 45 46 this._execEvent('beforeScrollStart'); 47 },
1 if ( utils.eventType[e.type] != 1 ) { 2 if ( e.button !== 0 ) { 3 return; 4 } 5 }
首先一段代碼特別針對非touch事件進行了處理,其中的意圖暫時不明,應該是只有點擊鼠標左鍵的情況下才會觸發下面邏輯
1 if ( !this.enabled || (this.initiated && utils.eventType[e.type] !== this.initiated) ) { 2 return; 3 }
第二段代碼做了一次是否開始的驗證,this.enabled應該是程序功能總開關,this.initiated為首次觸發touchStart的事件類型
可能是mousedown,touchstart或者其他,如果兩次不等的話,這里也會終止流程(此段代碼確實不明意圖)
// This should find all Android browsers lower than build 535.19 (both stock browser and webview) me.isBadAndroid = /Android/.test(window.navigator.appVersion) && !(/Chrome\/\d/.test(window.navigator.appVersion));
1 if ( this.options.preventDefault && !utils.isBadAndroid && !utils.preventDefaultException(e.target, this.options.preventDefaultException) ) { 2 e.preventDefault(); 3 }
第三段做了一些兼容性操作,各位可以看到只有這里有preventDefault的操作,如果默寫dom元素在你手觸碰會有默認的事件,比如文本框,便會觸發
但是我們默認是阻止的,而在一些android設備上市必須阻止的,這里建議都阻止
1 var point = e.touches ? e.touches[0] : e, 2 pos; 3 4 this.initiated = utils.eventType[e.type]; 5 this.moved = false; 6 this.distX = 0; 7 this.distY = 0; 8 this.directionX = 0; 9 this.directionY = 0; 10 this.directionLocked = 0;
接下來進行了一些初始化屬性的定義,其中比較重要的是
① point做了最簡單的兼容性處理
② this.moved你想知道現在控件是不是在拖動就看他了
然后這里執行了一個方法:_transitionTime
1 _transitionTime: function (time) { 2 time = time || 0; 3 4 this.scrollerStyle[utils.style.transitionDuration] = time + 'ms'; 5 6 if (!time && utils.isBadAndroid) { 7 this.scrollerStyle[utils.style.transitionDuration] = '0.001s'; 8 } 9 10 if (this.indicators) { 11 for (var i = this.indicators.length; i--; ) { 12 this.indicators[i].transitionTime(time); 13 } 14 } 15 16 // INSERT POINT: _transitionTime 17 },
utils具有以下style屬性:
1 me.extend(me.style = {}, { 2 transform: _transform, 3 transitionTimingFunction: _prefixStyle('transitionTimingFunction'), 4 transitionDuration: _prefixStyle('transitionDuration'), 5 transitionDelay: _prefixStyle('transitionDelay'), 6 transformOrigin: _prefixStyle('transformOrigin') 7 });
這里為其設置了適合放棄瀏覽器的運動時間屬性,就是簡單的兼容處理
在有些android瀏覽器上,這個使用是有問題的,所以就直接當沒傳時間,直接給了個0.001s
其中的indicators就是我們的滾動條,這里既然涉及到了,我們也暫時不管,因為涉及到滾動條的篇幅也不小,我們暫時不關注
這個方法也涉及滾動條相關,我們這里先簡單提一下,后面在補充,現在繼續看下面的邏輯
再下面就開始真正初始化信息,這些信息在以下會被用到
1 this.startTime = utils.getTime(); 2 3 if ( this.options.useTransition && this.isInTransition ) { 4 this.isInTransition = false; 5 pos = this.getComputedPosition(); 6 this._translate(Math.round(pos.x), Math.round(pos.y)); 7 this._execEvent('scrollEnd'); 8 } else if ( !this.options.useTransition && this.isAnimating ) { 9 this.isAnimating = false; 10 this._execEvent('scrollEnd'); 11 } 12 13 this.startX = this.x; 14 this.startY = this.y; 15 this.absStartX = this.x; 16 this.absStartY = this.y; 17 this.pointX = point.pageX; 18 this.pointY = point.pageY;
首先記錄了手指觸屏屏幕的時間,而后記錄手指所處的位置,其中有兩個if語句需要我們注意,這里的代碼還是相當關鍵的
這段話的意義是告訴我們,如果我們當前正在運動,而此時觸屏了,那么就觸發scrollEnd事件停止動畫(這里非常關鍵)
其中若是使用了CSS3的屬性實現動畫會做一些特別的處理,這里的this.isAnimating = false 是一個關鍵點,各位要注意
他在首次為undefined(我覺得這種屬性應該給他一個初始化值false),運動過程中為true,運動結束為false
這里再提一下若是使用CSS3的話,會馬上讓dom移動到特定位置,然后停止動畫
1 _translate: function (x, y) { 2 if ( this.options.useTransform ) { 3 this.scrollerStyle[utils.style.transform] = 'translate(' + x + 'px,' + y + 'px)' + this.translateZ; 4 5 } else { 6 x = Math.round(x); 7 y = Math.round(y); 8 this.scrollerStyle.left = x + 'px'; 9 this.scrollerStyle.top = y + 'px'; 10 } 11 this.x = x; 12 this.y = y; 13 if ( this.indicators ) { 14 for ( var i = this.indicators.length; i--; ) { 15 this.indicators[i].updatePosition(); 16 } 17 } 18 // INSERT POINT: _translate 19 },
下面的代碼依舊在操作滾動條,不用現在關注,這里將信息全部寫入了scrollerStyle對象,同事dom style屬性的引用,這里就直接給賦值了
然我們來看看this._execEvent('scrollEnd');這段代碼
首先_execEvent是用於觸發存儲在this._event數組中的事件的方法,然后我們只看scrollEnd即可,這里的事件機制,我們提到后面來說
很奇怪的是,這里有觸發事件的代碼卻沒有注冊的代碼,這是因為這個接口應該是開放給用戶的,然后這里的beforeScrollStart也是開放給用戶注冊事件的
到此touchstart相關事件就結束了,我們接下來看move事件
move
1 _move: function (e) { 2 if (!this.enabled || utils.eventType[e.type] !== this.initiated) { 3 return; 4 } 5 6 if (this.options.preventDefault) { // increases performance on Android? TODO: check! 7 e.preventDefault(); 8 } 9 10 var point = e.touches ? e.touches[0] : e, 11 deltaX = point.pageX - this.pointX, 12 deltaY = point.pageY - this.pointY, 13 timestamp = utils.getTime(), 14 newX, newY, 15 absDistX, absDistY; 16 17 this.pointX = point.pageX; 18 this.pointY = point.pageY; 19 20 this.distX += deltaX; 21 this.distY += deltaY; 22 absDistX = Math.abs(this.distX); 23 absDistY = Math.abs(this.distY); 24 25 // We need to move at least 10 pixels for the scrolling to initiate 26 if (timestamp - this.endTime > 300 && (absDistX < 10 && absDistY < 10)) { 27 return; 28 } 29 30 // If you are scrolling in one direction lock the other 31 if (!this.directionLocked && !this.options.freeScroll) { 32 if (absDistX > absDistY + this.options.directionLockThreshold) { 33 this.directionLocked = 'h'; // lock horizontally 34 } else if (absDistY >= absDistX + this.options.directionLockThreshold) { 35 this.directionLocked = 'v'; // lock vertically 36 } else { 37 this.directionLocked = 'n'; // no lock 38 } 39 } 40 41 if (this.directionLocked == 'h') { 42 if (this.options.eventPassthrough == 'vertical') { 43 e.preventDefault(); 44 } else if (this.options.eventPassthrough == 'horizontal') { 45 this.initiated = false; 46 return; 47 } 48 49 deltaY = 0; 50 } else if (this.directionLocked == 'v') { 51 if (this.options.eventPassthrough == 'horizontal') { 52 e.preventDefault(); 53 } else if (this.options.eventPassthrough == 'vertical') { 54 this.initiated = false; 55 return; 56 } 57 58 deltaX = 0; 59 } 60 61 deltaX = this.hasHorizontalScroll ? deltaX : 0; 62 deltaY = this.hasVerticalScroll ? deltaY : 0; 63 64 newX = this.x + deltaX; 65 newY = this.y + deltaY; 66 67 // Slow down if outside of the boundaries 68 if (newX > 0 || newX < this.maxScrollX) { 69 newX = this.options.bounce ? this.x + deltaX / 3 : newX > 0 ? 0 : this.maxScrollX; 70 } 71 if (newY > 0 || newY < this.maxScrollY) { 72 newY = this.options.bounce ? this.y + deltaY / 3 : newY > 0 ? 0 : this.maxScrollY; 73 } 74 75 this.directionX = deltaX > 0 ? -1 : deltaX < 0 ? 1 : 0; 76 this.directionY = deltaY > 0 ? -1 : deltaY < 0 ? 1 : 0; 77 78 if (!this.moved) { 79 this._execEvent('scrollStart'); 80 } 81 82 this.moved = true; 83 84 this._translate(newX, newY); 85 86 /* REPLACE START: _move */ 87 88 if (timestamp - this.startTime > 300) { 89 this.startTime = timestamp; 90 this.startX = this.x; 91 this.startY = this.y; 92 } 93 94 /* REPLACE END: _move */ 95 96 },
move為功能第二個階段,首先仍然是做全局開關檢測,如果不通過就直接給干掉
1 if (!this.enabled || utils.eventType[e.type] !== this.initiated) { 2 return; 3 } 4 5 if (this.options.preventDefault) { // increases performance on Android? TODO: check! 6 e.preventDefault(); 7 }
然后這個時候必須要將瀏覽器默認事件搞掉,否則你滾動時候,如果body跟着一起滾動就麻煩了
PS:Android下有些瀏覽器preventDefault並不能阻止瀏覽器滾動,這事情很煩
接下來就是記錄當前移動數據,為dom移動做准備了:
1 var point = e.touches ? e.touches[0] : e, 2 deltaX = point.pageX - this.pointX, 3 deltaY = point.pageY - this.pointY, 4 timestamp = utils.getTime(), 5 newX, newY, 6 absDistX, absDistY; 7 8 this.pointX = point.pageX; 9 this.pointY = point.pageY; 10 11 this.distX += deltaX; 12 this.distY += deltaY; 13 absDistX = Math.abs(this.distX); 14 absDistY = Math.abs(this.distY);
首先記錄了當前鼠標位置,而后記錄移動位置后,重置當前鼠標位置,然后這里做了一個判斷,這個判斷是如果我們手指一直停到一個位置不動的話,就給他終止了
這里也做了一個優化,為了防止瀏覽器不停的重繪嗎,一定是移動10px以上才真正的移動
1 if (!this.directionLocked && !this.options.freeScroll) { 2 if (absDistX > absDistY + this.options.directionLockThreshold) { 3 this.directionLocked = 'h'; // lock horizontally 4 } else if (absDistY >= absDistX + this.options.directionLockThreshold) { 5 this.directionLocked = 'v'; // lock vertically 6 } else { 7 this.directionLocked = 'n'; // no lock 8 } 9 }
這里做了一個判斷,讓DOM朝一個方向運動即可,因為我們關注的是Y方向,這里可以暫時不予關注
然后開始計算新位置了,這里要開始移動了哦(注意:這里做了一個判斷如果超出邊界的話,拖動率要減低)
1 newX = this.x + deltaX; 2 newY = this.y + deltaY; 3 4 // Slow down if outside of the boundaries 5 if (newX > 0 || newX < this.maxScrollX) { 6 newX = this.options.bounce ? this.x + deltaX / 3 : newX > 0 ? 0 : this.maxScrollX; 7 } 8 if (newY > 0 || newY < this.maxScrollY) { 9 newY = this.options.bounce ? this.y + deltaY / 3 : newY > 0 ? 0 : this.maxScrollY; 10 }
我們在touchstart時候將this.moved設置為了false,這里就觸發一個scrollSatrt事件后將其還原為true,所以這個事件只會觸發一次,
這個事件同樣是開放給用戶的,iScroll本身並未注冊任何事件
1 if (timestamp - this.startTime > 300) { 2 this.startTime = timestamp; 3 this.startX = this.x; 4 this.startY = this.y; 5 }
每300ms會重置一次當前位置以及開始時間,這個就是為什么我們在抓住不放很久突然丟開仍然有長距離移動的原因,這個比較精妙哦!
最后我們說下其中的_translate方法,這個方法用於移動DOM,這種封裝的思想很不錯的,值得借鑒,現在就進入關鍵點了touchend
end
1 _end: function (e) { 2 if (!this.enabled || utils.eventType[e.type] !== this.initiated) { 3 return; 4 } 5 6 if (this.options.preventDefault && !utils.preventDefaultException(e.target, this.options.preventDefaultException)) { 7 e.preventDefault(); 8 } 9 10 var point = e.changedTouches ? e.changedTouches[0] : e, 11 momentumX, 12 momentumY, 13 duration = utils.getTime() - this.startTime, 14 newX = Math.round(this.x), 15 newY = Math.round(this.y), 16 distanceX = Math.abs(newX - this.startX), 17 distanceY = Math.abs(newY - this.startY), 18 time = 0, 19 easing = ''; 20 21 this.isInTransition = 0; 22 this.initiated = 0; 23 this.endTime = utils.getTime(); 24 25 // reset if we are outside of the boundaries 26 if (this.resetPosition(this.options.bounceTime)) { 27 return; 28 } 29 30 this.scrollTo(newX, newY); // ensures that the last position is rounded 31 32 // we scrolled less than 10 pixels 33 if (!this.moved) { 34 if (this.options.tap) { 35 utils.tap(e, this.options.tap); 36 } 37 38 if (this.options.click) { 39 utils.click(e); 40 } 41 42 this._execEvent('scrollCancel'); 43 return; 44 } 45 46 if (this._events.flick && duration < 200 && distanceX < 100 && distanceY < 100) { 47 this._execEvent('flick'); 48 return; 49 } 50 51 // start momentum animation if needed 52 if (this.options.momentum && duration < 300) { 53 momentumX = this.hasHorizontalScroll ? utils.momentum(this.x, this.startX, duration, this.maxScrollX, this.options.bounce ? this.wrapperWidth : 0) : { destination: newX, duration: 0 }; 54 momentumY = this.hasVerticalScroll ? utils.momentum(this.y, this.startY, duration, this.maxScrollY, this.options.bounce ? this.wrapperHeight : 0) : { destination: newY, duration: 0 }; 55 newX = momentumX.destination; 56 newY = momentumY.destination; 57 time = Math.max(momentumX.duration, momentumY.duration); 58 this.isInTransition = 1; 59 } 60 61 62 if (this.options.snap) { 63 var snap = this._nearestSnap(newX, newY); 64 this.currentPage = snap; 65 time = this.options.snapSpeed || Math.max( 66 Math.max( 67 Math.min(Math.abs(newX - snap.x), 1000), 68 Math.min(Math.abs(newY - snap.y), 1000) 69 ), 300); 70 newX = snap.x; 71 newY = snap.y; 72 73 this.directionX = 0; 74 this.directionY = 0; 75 easing = this.options.bounceEasing; 76 } 77 78 // INSERT POINT: _end 79 80 if (newX != this.x || newY != this.y) { 81 // change easing function when scroller goes out of the boundaries 82 if (newX > 0 || newX < this.maxScrollX || newY > 0 || newY < this.maxScrollY) { 83 easing = utils.ease.quadratic; 84 } 85 86 this.scrollTo(newX, newY, time, easing); 87 return; 88 } 89 90 this._execEvent('scrollEnd'); 91 },
開始我們就說過,touchend為這個控件一個關鍵點與難點,現在我們就來啃一啃,這個看完了,iScroll核心部分也就結束了,后面就只需要拆解分析即可
首先仍然是一點初始化操作
1 if (!this.enabled || utils.eventType[e.type] !== this.initiated) { 2 return; 3 } 4 5 if (this.options.preventDefault && !utils.preventDefaultException(e.target, this.options.preventDefaultException)) { 6 e.preventDefault(); 7 }
而后逐步好戲上場了,在手指離開前做了狀態保存
1 var point = e.changedTouches ? e.changedTouches[0] : e, 2 momentumX, 3 momentumY, 4 duration = utils.getTime() - this.startTime, 5 newX = Math.round(this.x), 6 newY = Math.round(this.y), 7 distanceX = Math.abs(newX - this.startX), 8 distanceY = Math.abs(newY - this.startY), 9 time = 0, 10 easing = ''; 11 12 this.isInTransition = 0; 13 this.initiated = 0; 14 this.endTime = utils.getTime();
duration是當前拖動的事件,這里可不是手指觸屏到離開哦,因為move時候每300ms變了一次
PS:這里想象一下如果我們想要快速的滑動是不是觸屏屏幕很快呢,而我們一直拖動DOM在最后也是有可能想讓他快速移動的
記錄當前x,y位置記錄當前移動位置distanceY,然后重置結束時間,這里有一個resetPosition方法:
1 resetPosition: function (time) { 2 var x = this.x, 3 y = this.y; 4 5 time = time || 0; 6 7 if ( !this.hasHorizontalScroll || this.x > 0 ) { 8 x = 0; 9 } else if ( this.x < this.maxScrollX ) { 10 x = this.maxScrollX; 11 } 12 13 if ( !this.hasVerticalScroll || this.y > 0 ) { 14 y = 0; 15 } else if ( this.y < this.maxScrollY ) { 16 y = this.maxScrollY; 17 } 18 19 if ( x == this.x && y == this.y ) { 20 return false; 21 } 22 23 this.scrollTo(x, y, time, this.options.bounceEasing); 24 25 return true; 26 },
他是記錄我們是不是已經離開了邊界了,如果離開邊界了就不會執行后面邏輯,而直接重置DOM位置,這里還用到了我們的scrollTo方法,該方法尤其關鍵
scrollTo
1 scrollTo: function (x, y, time, easing) { 2 easing = easing || utils.ease.circular; 3 4 this.isInTransition = this.options.useTransition && time > 0; 5 6 if ( !time || (this.options.useTransition && easing.style) ) { 7 this._transitionTimingFunction(easing.style); 8 this._transitionTime(time); 9 this._translate(x, y); 10 } else { 11 this._animate(x, y, time, easing.fn); 12 } 13 },
這個方法是此處一個重要的方法,傳入距離與時間后,他就會高高興興的移動到對應位置
如果啟用了CSS3的動畫,便會使用CSS3動畫方式進行動畫(這個動畫我們下期再說),否則使用_animate方法(js實現方案)
1 _animate: function (destX, destY, duration, easingFn) { 2 var that = this, 3 startX = this.x, 4 startY = this.y, 5 startTime = utils.getTime(), 6 destTime = startTime + duration; 7 8 function step () { 9 var now = utils.getTime(), 10 newX, newY, 11 easing; 12 13 if ( now >= destTime ) { 14 that.isAnimating = false; 15 that._translate(destX, destY); 16 17 if ( !that.resetPosition(that.options.bounceTime) ) { 18 that._execEvent('scrollEnd'); 19 } 20 21 return; 22 } 23 24 now = ( now - startTime ) / duration; 25 easing = easingFn(now); 26 newX = ( destX - startX ) * easing + startX; 27 newY = ( destY - startY ) * easing + startY; 28 that._translate(newX, newY); 29 30 if ( that.isAnimating ) { 31 rAF(step); 32 } 33 } 34 35 this.isAnimating = true; 36 step(); 37 },
這里用到了前文說描述的settimeout實現動畫方案,這里有一點需要我們回到start部分重新思考,為什么CSS停止了動畫?
原因是因為transitionend事件
transitionend 事件會在 CSS transition 結束后觸發. 當transition完成前移除transition時,比如移除css的transition-property 屬性,事件將不會被觸發.
1 _transitionEnd: function (e) { 2 if ( e.target != this.scroller || !this.isInTransition ) { 3 return; 4 } 5 6 this._transitionTime(); 7 if ( !this.resetPosition(this.options.bounceTime) ) { 8 this.isInTransition = false; 9 this._execEvent('scrollEnd'); 10 } 11 },
所以,我們第二次touchstart時候,便高高興興停止了動畫(之一_transitionTime未傳time時候會重置時間),所以先取消動畫再移動位置
於是繼續回到我們的end事件,
1 this.scrollTo(newX, newY);
如果沒有超出邊界便滑動到應該去的位置(這里有動畫哦)
點擊情況
當然,我們手指可能當前只不過想點擊而已,這個時候就要觸發相關的點擊事件了,如果需要獲取焦點,便獲取焦點
PS:他這里還模擬的fastclick想提升響應速度,但是他這樣會引起大量BUG
1 if (!this.moved) { 2 if (this.options.tap) { 3 utils.tap(e, this.options.tap); 4 } 5 6 if (this.options.click) { 7 utils.click(e); 8 } 9 10 this._execEvent('scrollCancel'); 11 return; 12 } 13 14 if (this._events.flick && duration < 200 && distanceX < 100 && distanceY < 100) { 15 this._execEvent('flick'); 16 return; 17 }
運動參數
第一步的scrollTo其實可以放到move里面去,后面就用到了我們上文所說,根據動力加速度計算出來的動畫參數:
1 if (this.options.momentum && duration < 300) { 2 momentumX = this.hasHorizontalScroll ? utils.momentum(this.x, this.startX, duration, this.maxScrollX, this.options.bounce ? this.wrapperWidth : 0) : { destination: newX, duration: 0 }; 3 momentumY = this.hasVerticalScroll ? utils.momentum(this.y, this.startY, duration, this.maxScrollY, this.options.bounce ? this.wrapperHeight : 0) : { destination: newY, duration: 0 }; 4 newX = momentumX.destination; 5 newY = momentumY.destination; 6 time = Math.max(momentumX.duration, momentumY.duration); 7 this.isInTransition = 1; 8 }
那個snap不必關注,直接看下面,在此使用
1 this.scrollTo(newX, newY, time, easing);
開始運動,最后觸發scrollend事件,這里如果超出邊界會執行resetPosition方法還原的,不必關心
由此,我們幾大核心事件點便學習結束了,輕松愉快哈
結語
今天學習了iScroll的幾個核心點,我們下次來說下他的滾動條以及事件機制相關,整個iScroll就七七八八了
