【iScroll源碼學習01】準備階段

前言

咱們昨天初步瞭解了爲何會出現iScroll:【SPA】移動站點APP化研究之上中下頁面的iScroll化(上),而後簡單的寫了一個demo來模擬iScroll,其中瞭解到瞭如下知識點:javascript

① viewport相關知識點(device-width等)css

② CSS3硬件加速html

③ 如何暫停CSS動畫java

④ e.preventDefault致使文本不能獲取焦點解決方案jquery

......web

固然,咱們寫的demo天然不能和iScroll自己的代碼比肩,可是demo過程當中咱們也大概瞭解了iScroll代碼過程當中須要注意的一些問題chrome

因而,今天讓咱們進入iScroll的學習吧ubuntu

初探iScroll

  1 <!DOCTYPE html>
  2 <html>
  3 <head>
  4 <meta charset="utf-8">
  5 <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=0, minimum-scale=1.0, maximum-scale=1.0">
  6 
  7 <title>iScroll demo: scrollbars</title>
  8 
  9 <script type="text/javascript" src="../../build/iscroll.js"></script>
 10 
 11 
 12 <script type="text/javascript">
 13 
 14 var myScroll;
 15 
 16 function loaded () {
 17     myScroll = new IScroll('#wrapper', {
 18         scrollbars: true,
 19         mouseWheel: true,
 20         interactiveScrollbars: true,
 21         shrinkScrollbars: 'scale',
 22         fadeScrollbars: true
 23     });
 24 }
 25 
 26 document.addEventListener('touchmove', function (e) { e.preventDefault(); }, false);
 27 
 28 </script>
 29 
 30 <style type="text/css">
 31 * {
 32     -webkit-box-sizing: border-box;
 33     -moz-box-sizing: border-box;
 34     box-sizing: border-box;
 35 }
 36 
 37 html {
 38     -ms-touch-action: none;
 39 }
 40 
 41 body,ul,li {
 42     padding: 0;
 43     margin: 0;
 44     border: 0;
 45 }
 46 
 47 body {
 48     font-size: 12px;
 49     font-family: ubuntu, helvetica, arial;
 50     overflow: hidden; /* this is important to prevent the whole page to bounce */
 51 }
 52 
 53 #header {
 54     position: absolute;
 55     z-index: 2;
 56     top: 0;
 57     left: 0;
 58     width: 100%;
 59     height: 45px;
 60     line-height: 45px;
 61     background: #CD235C;
 62     padding: 0;
 63     color: #eee;
 64     font-size: 20px;
 65     text-align: center;
 66     font-weight: bold;
 67 }
 68 
 69 #footer {
 70     position: absolute;
 71     z-index: 2;
 72     bottom: 0;
 73     left: 0;
 74     width: 100%;
 75     height: 48px;
 76     background: #444;
 77     padding: 0;
 78     border-top: 1px solid #444;
 79 }
 80 
 81 #wrapper {
 82     position: absolute;
 83     z-index: 1;
 84     top: 45px;
 85     bottom: 48px;
 86     left: 0;
 87     width: 100%;
 88     background: #ccc;
 89     overflow: hidden;
 90 }
 91 
 92 #scroller {
 93     position: absolute;
 94     z-index: 1;
 95     -webkit-tap-highlight-color: rgba(0,0,0,0);
 96     width: 100%;
 97     -webkit-transform: translateZ(0);
 98     -moz-transform: translateZ(0);
 99     -ms-transform: translateZ(0);
100     -o-transform: translateZ(0);
101     transform: translateZ(0);
102     -webkit-touch-callout: none;
103     -webkit-user-select: none;
104     -moz-user-select: none;
105     -ms-user-select: none;
106     user-select: none;
107     -webkit-text-size-adjust: none;
108     -moz-text-size-adjust: none;
109     -ms-text-size-adjust: none;
110     -o-text-size-adjust: none;
111     text-size-adjust: none;
112 }
113 
114 #scroller ul {
115     list-style: none;
116     padding: 0;
117     margin: 0;
118     width: 100%;
119     text-align: left;
120 }
121 
122 #scroller li {
123     padding: 0 10px;
124     height: 40px;
125     line-height: 40px;
126     border-bottom: 1px solid #ccc;
127     border-top: 1px solid #fff;
128     background-color: #fafafa;
129     font-size: 14px;
130 }
131 
132 </style>
133 </head>
134 <body onload="loaded()">
135 <div id="header">iScroll</div>
136 
137 <div id="wrapper">
138     <div id="scroller">
139         <ul>
140             <li>Pretty row 1</li>
141             <li>Pretty row 2</li>
142             <li>Pretty row 3</li>
143             <li>Pretty row 4</li>
144             <li>Pretty row 5</li>
145             <li>Pretty row 6</li>
146             <li>Pretty row 7</li>
147             <li>Pretty row 8</li>
148             <li>Pretty row 9</li>
149             <li>Pretty row 10</li>
150             <li>Pretty row 11</li>
151             <li>Pretty row 12</li>
152             <li>Pretty row 13</li>
153             <li>Pretty row 14</li>
154             <li>Pretty row 15</li>
155             <li>Pretty row 16</li>
156             <li>Pretty row 17</li>
157             <li>Pretty row 18</li>
158             <li>Pretty row 19</li>
159             <li>Pretty row 20</li>
160             <li>Pretty row 21</li>
161             <li>Pretty row 22</li>
162             <li>Pretty row 23</li>
163             <li>Pretty row 24</li>
164             <li>Pretty row 25</li>
165             <li>Pretty row 26</li>
166             <li>Pretty row 27</li>
167             <li>Pretty row 28</li>
168             <li>Pretty row 29</li>
169             <li>Pretty row 30</li>
170             <li>Pretty row 31</li>
171             <li>Pretty row 32</li>
172             <li>Pretty row 33</li>
173             <li>Pretty row 34</li>
174             <li>Pretty row 35</li>
175             <li>Pretty row 36</li>
176             <li>Pretty row 37</li>
177             <li>Pretty row 38</li>
178             <li>Pretty row 39</li>
179             <li>Pretty row 40</li>
180             <li>Pretty row 41</li>
181             <li>Pretty row 42</li>
182             <li>Pretty row 43</li>
183             <li>Pretty row 44</li>
184             <li>Pretty row 45</li>
185             <li>Pretty row 46</li>
186             <li>Pretty row 47</li>
187             <li>Pretty row 48</li>
188             <li>Pretty row 49</li>
189             <li>Pretty row 50</li>
190 
191       <li>Pretty row 1</li>
192             <li>Pretty row 2</li>
193             <li>Pretty row 3</li>
194             <li>Pretty row 4</li>
195             <li>Pretty row 5</li>
196             <li>Pretty row 6</li>
197             <li>Pretty row 7</li>
198             <li>Pretty row 8</li>
199             <li>Pretty row 9</li>
200             <li>Pretty row 10</li>
201             <li>Pretty row 11</li>
202             <li>Pretty row 12</li>
203             <li>Pretty row 13</li>
204             <li>Pretty row 14</li>
205             <li>Pretty row 15</li>
206             <li>Pretty row 16</li>
207             <li>Pretty row 17</li>
208             <li>Pretty row 18</li>
209             <li>Pretty row 19</li>
210             <li>Pretty row 20</li>
211             <li>Pretty row 21</li>
212             <li>Pretty row 22</li>
213             <li>Pretty row 23</li>
214             <li>Pretty row 24</li>
215             <li>Pretty row 25</li>
216             <li>Pretty row 26</li>
217             <li>Pretty row 27</li>
218             <li>Pretty row 28</li>
219             <li>Pretty row 29</li>
220             <li>Pretty row 30</li>
221             <li>Pretty row 31</li>
222             <li>Pretty row 32</li>
223             <li>Pretty row 33</li>
224             <li>Pretty row 34</li>
225             <li>Pretty row 35</li>
226             <li>Pretty row 36</li>
227             <li>Pretty row 37</li>
228             <li>Pretty row 38</li>
229             <li>Pretty row 39</li>
230             <li>Pretty row 40</li>
231             <li>Pretty row 41</li>
232             <li>Pretty row 42</li>
233             <li>Pretty row 43</li>
234             <li>Pretty row 44</li>
235             <li>Pretty row 45</li>
236             <li>Pretty row 46</li>
237             <li>Pretty row 47</li>
238             <li>Pretty row 48</li>
239             <li>Pretty row 49</li>
240             <li>Pretty row 50</li>
241 
242       <li>Pretty row 1</li>
243             <li>Pretty row 2</li>
244             <li>Pretty row 3</li>
245             <li>Pretty row 4</li>
246             <li>Pretty row 5</li>
247             <li>Pretty row 6</li>
248             <li>Pretty row 7</li>
249             <li>Pretty row 8</li>
250             <li>Pretty row 9</li>
251             <li>Pretty row 10</li>
252             <li>Pretty row 11</li>
253             <li>Pretty row 12</li>
254             <li>Pretty row 13</li>
255             <li>Pretty row 14</li>
256             <li>Pretty row 15</li>
257             <li>Pretty row 16</li>
258             <li>Pretty row 17</li>
259             <li>Pretty row 18</li>
260             <li>Pretty row 19</li>
261             <li>Pretty row 20</li>
262             <li>Pretty row 21</li>
263             <li>Pretty row 22</li>
264             <li>Pretty row 23</li>
265             <li>Pretty row 24</li>
266             <li>Pretty row 25</li>
267             <li>Pretty row 26</li>
268             <li>Pretty row 27</li>
269             <li>Pretty row 28</li>
270             <li>Pretty row 29</li>
271             <li>Pretty row 30</li>
272             <li>Pretty row 31</li>
273             <li>Pretty row 32</li>
274             <li>Pretty row 33</li>
275             <li>Pretty row 34</li>
276             <li>Pretty row 35</li>
277             <li>Pretty row 36</li>
278             <li>Pretty row 37</li>
279             <li>Pretty row 38</li>
280             <li>Pretty row 39</li>
281             <li>Pretty row 40</li>
282             <li>Pretty row 41</li>
283             <li>Pretty row 42</li>
284             <li>Pretty row 43</li>
285             <li>Pretty row 44</li>
286             <li>Pretty row 45</li>
287             <li>Pretty row 46</li>
288             <li>Pretty row 47</li>
289             <li>Pretty row 48</li>
290             <li>Pretty row 49</li>
291             <li>Pretty row 50</li>
292 
293       <li>Pretty row 1</li>
294             <li>Pretty row 2</li>
295             <li>Pretty row 3</li>
296             <li>Pretty row 4</li>
297             <li>Pretty row 5</li>
298             <li>Pretty row 6</li>
299             <li>Pretty row 7</li>
300             <li>Pretty row 8</li>
301             <li>Pretty row 9</li>
302             <li>Pretty row 10</li>
303             <li>Pretty row 11</li>
304             <li>Pretty row 12</li>
305             <li>Pretty row 13</li>
306             <li>Pretty row 14</li>
307             <li>Pretty row 15</li>
308             <li>Pretty row 16</li>
309             <li>Pretty row 17</li>
310             <li>Pretty row 18</li>
311             <li>Pretty row 19</li>
312             <li>Pretty row 20</li>
313             <li>Pretty row 21</li>
314             <li>Pretty row 22</li>
315             <li>Pretty row 23</li>
316             <li>Pretty row 24</li>
317             <li>Pretty row 25</li>
318             <li>Pretty row 26</li>
319             <li>Pretty row 27</li>
320             <li>Pretty row 28</li>
321             <li>Pretty row 29</li>
322             <li>Pretty row 30</li>
323             <li>Pretty row 31</li>
324             <li>Pretty row 32</li>
325             <li>Pretty row 33</li>
326             <li>Pretty row 34</li>
327             <li>Pretty row 35</li>
328             <li>Pretty row 36</li>
329             <li>Pretty row 37</li>
330             <li>Pretty row 38</li>
331             <li>Pretty row 39</li>
332             <li>Pretty row 40</li>
333             <li>Pretty row 41</li>
334             <li>Pretty row 42</li>
335             <li>Pretty row 43</li>
336             <li>Pretty row 44</li>
337             <li>Pretty row 45</li>
338             <li>Pretty row 46</li>
339             <li>Pretty row 47</li>
340             <li>Pretty row 48</li>
341             <li>Pretty row 49</li>
342             <li>Pretty row 50</li>
343         </ul>
344     </div>
345 </div>
346 
347 <div id="footer"></div>
348 </body>
349 </html>
View Code

http://sandbox.runjs.cn/show/pscjy3a3瀏覽器

下面是他初始化時候的核心代碼:緩存

 1 var myScroll;
 2 function loaded () {
 3     myScroll = new IScroll('#wrapper', {
 4         scrollbars: true,
 5         mouseWheel: true,
 6         interactiveScrollbars: true,
 7         shrinkScrollbars: 'scale',
 8         fadeScrollbars: true
 9     });
10 }
11 document.addEventListener('touchmove', function (e) { e.preventDefault(); }, false);

真不得不說,這個滑動起來感受挺不錯的,第一感受有幾大特色:

① 順暢

② 連續拖動會加速滑動

③ 沒有BUG :)

看看他的構造函數,而後去網上找一點參數說明(Iscroll應用文檔):

hScroll: true, //是否水平滾動
vScroll: true, //是否垂直滾動
x: 0, //滾動水平初始位置
y: 0, //滾動垂直初始位置
snap: true, //值能夠爲true或是DOM元素的tagname,當爲true時,對齊的座標會根據可滾動的位置和滾動區域計算獲得可滑動幾頁,若是爲tagname,則滑動會對齊到元素上
bounce: true, //是否超過實際位置反彈
bounceLock: false, //當內容少於滾動是否能夠反彈,這個實際用處不大
momentum: true, //動量效果,拖動慣性
lockDirection: true, //當水平滾動和垂直滾動同時生效時,當拖動開始是否鎖定另外一邊的拖動
useTransform: true, //是否使用CSS形變
useTransition: false, //是否使用CSS變換
topOffset: 0, //已經滾動的基準值(通常狀況用不到)
checkDOMChanges: false, //是否自動檢測內容變化(這個檢測不是很準)

//Scrollbar相關參數,經過scrollbar這些參數能夠配置iscroll的滾動條,經過scrollbarClass能夠本身定義一套滾動條的樣式。
hScrollbar: true, //是否顯示水平滾動條
vScrollbar: true, //同上垂直滾動條
fixedScrollbar: isAndroid, //對andriod的fixed
hideScrollbar: isIDevice, //是否隱藏滾動條
fadeScrollbar: isIDevice && has3d, //滾動條是否漸隱漸顯
scrollbarClass: '', //自定義滾動條的樣式名

//Zoom放大相關的參數,經過它,對於一個固定顯示圖片區域的相似應用,能夠很是簡單的作到固定滾動,包括兩指放大的應用。
zoom: false, //默認是否放大
zoomMin: 1, //放大的最小倍數
zoomMax: 4, //最大倍數
doubleTapZoom: 2, //雙觸放大幾倍
wheelAction: 'scroll', //鼠標滾動行爲(還能夠是zoom)

//自定義Events相關參數 
onRefresh: null, //refresh 的回調,關於自身什麼時候調用refresh 後面會繼續談到
onBeforeScrollStart: function(e){ e.preventDefault(); }, //開始滾動前的時間回調,默認是阻止瀏覽器默認行爲
onScrollStart: null, //開始滾動的回調
onBeforeScrollMove: null, //在內容移動前的回調
onScrollMove: null, //內容移動的回調
onBeforeScrollEnd: null, //在滾動結束前的回調
onScrollEnd: null, //在滾動完成後的回調
onTouchEnd: null, //手離開屏幕後的回調
onDestroy: null, //銷燬實例的回調
onZoomStart: null, //開始放大前的回調
onZoom: null, //放大的回調
onZoomEnd: null //放大完成後的回調
Iscroll 提供的調用方法

destroy 
顧名思義,是用來銷燬你實例化的iScroll 實例,包括以前綁定的全部iscroll 事件。 

refresh 
這個方法很是有用,當你的滾動區域的內容發生改變 或是 滾動區域不正確,都用經過調用refresh 來使得iscroll 從新計算滾動的區域,包括滾動條,來使得iscroll 適合當前的dom。 

scrollTo 
這個方法接受4個參數 x, y, time, relative x 爲移動的x軸座標,y爲移動的y軸座標, time爲移動時間,relative表示是否相對當前位置。 

scrollToElement 
這個方法其實是對scrollTo的進一步封裝,接受兩個參數(el,time),el爲須要滾動到的元素引用,time爲滾動時間。 

scrollToPage 
此方法接受三個參數(pageX,pageY,time) 當滾動內容的高寬大於滾動範圍時,iscroll 會自動分頁,而後就能使用scrollToPage方法滾動到頁面。固然,當hscroll 爲false 的時候,不能左右滾動。pageX這個參數就失去效果 

disable 
調用這個方法會當即中止動畫滾動,而且把滾動位置還原成0,取消綁定touchmove, touchend、touchcancel事件。 

enable 
調用這個方法,使得iscroll恢復默認正常狀態 

stop 
當即中止動畫 

zoom 
改變內容的大小倍數,此方法接受4個參數,x,y,scale,time 分別表示的意思爲,放大的基準座標,以及放大倍數,動畫時間 

isReady 
當iscroll 沒有處於正在滾動,沒有移動過,沒有改變大小時,此值爲true

功能很是豐富啊,對於應用來講夠用了,可是一些功能我這裏用不到,就忽略了

功能很好,size爲48k,壓縮後稍微好一點,將近2000行的代碼,做爲基礎庫來講,有點大了,比整個zepto還大

並且整個庫的註釋寫的很差,好像壓根就沒寫......不知道閱讀上會不會有障礙,因而咱們進入源碼

iScroll筆記

requestAnimationFrame

1 var rAF = window.requestAnimationFrame    ||
2     window.webkitRequestAnimationFrame    ||
3     window.mozRequestAnimationFrame        ||
4     window.oRequestAnimationFrame        ||
5     window.msRequestAnimationFrame        ||
6     function (callback) { window.setTimeout(callback, 1000 / 60); };

這段代碼是要作能力檢測的,這裏來講一下requestAnimationFrame這個東西(參考:http://www.kimhou.com/?p=155

在jquery中javascript動畫是經過定時器(settimeout)實現的,沒一個時間點改變一點style,而CSS3後便推出了transition以及animation開始實現動畫

咱們昨天提到的硬件加速,也是CSS3相關的東西。CSS3動畫效率與順暢度比Js高,因此如今動畫開始楚河漢界了

js的好處是能夠很好的控制動畫狀態、css動畫帶來的性能較高,可是控制度就低一點(是很低)

定時器實現動畫

 1 <html xmlns="http://www.w3.org/1999/xhtml">
 2 <head>
 3   <title></title>
 4 </head>
 5 <body>
 6   <div id="el" style="position: absolute;">
 7     Javascript時鐘實現動畫
 8   </div>
 9   <script src="zepto.js" type="text/javascript"></script>
10   <script type="text/javascript">
11     var pos = 0;
12     var final = 200;
13     var dir = 0;
14     var el = $('#el');
15     function upLeft() {
16       var left = parseInt(el.css('left')) || 0;
17       if (left >= final) dir = 1;
18       if (left <= pos) dir = 0;
19 
20       if (dir == 0) {
21         left++;
22         el.css('left', left + 'px');
23         setTimeout(upLeft);
24       } else {
25         left--;
26         el.css('left', left + 'px');
27         setTimeout(upLeft);
28       }
29     }
30     upLeft();
31   </script>
32 </body>
33 </html>

效果見下面

這即是使用javascript實現的一個最簡單的動畫,各位看到了,裏面的定時器不停的在運動,性能不差的話我就更名叫素還真了

這個階段,比較棘手的問題每每在延遲的計算,間隔要短因此動畫順暢,可是瀏覽器渲染也得耗費時間,這個就要求每次變化留給瀏覽器的時間夠長了(60HZ/75Hz)

因此以前javascript的間隔通常爲20左右,這個時候的動畫比較流暢,這個數字與瀏覽器的頻率比較接近(1000/60)

function (callback) { window.setTimeout(callback, 1000 / 60); }

可是經過前面對時鐘的學習,咱們知道settimeout只是將回調函數加入UI線程隊列,那麼同一時間有多個動畫待執行的話,延遲就發生了,效果也會打折扣

這裏原來用js實現坦克大戰的朋友就會有所體會了

javascript問題解決

CSS的transition與animations的優點在於瀏覽器知道哪些動畫將會發生,因此動畫會獲得正確的間隔來刷新UI(javascript固然不知道)

因而這裏就多了一個方法:RequestAnimationFrame,他能夠告訴瀏覽器本身要執行動畫了,因而js的動畫事實上獲得了優化

RequestAnimationFrame接受一個參數,也就是屏幕重繪前會調用的函數,這個函數用來改變dom樣式,這個方法使用有點相似於settimeout

 1 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
 2 <html xmlns="http://www.w3.org/1999/xhtml">
 3 <head>
 4   <title></title>
 5 </head>
 6 <body>
 7 
 8   <div id="el" style="position: absolute;">
 9     Javascript時鐘實現動畫
10   </div>
11   <script src="zepto.js" type="text/javascript"></script>
12   <script type="text/javascript">
13     var rAF = window.requestAnimationFrame ||
14     window.webkitRequestAnimationFrame ||
15     window.mozRequestAnimationFrame ||
16     window.oRequestAnimationFrame ||
17     window.msRequestAnimationFrame ||
18     function (callback) { window.setTimeout(callback, 1000 / 60); };
19     var pos = 0;
20     var final = 200;
21     var dir = 0;
22     var el = $('#el');
23     function upLeft() {
24       var left = parseInt(el.css('left')) || 0;
25       if (left >= final) dir = 1;
26       if (left <= pos) dir = 0;
27 
28       if (dir == 0) {
29         left++;
30         el.css('left', left + 'px');
31         rAF(upLeft);
32       } else {
33         left--;
34         el.css('left', left + 'px');
35         rAF(upLeft);
36       }
37     }
38     upLeft();
39   </script>
40 </body>
41 </html>

這個動畫將會有不同的感覺:

動畫效果相關,各位本身去感覺,代碼各位能夠本身調整。如此這個方法其實就是作javascript動畫處理優化方案的

PS:尼瑪,iScroll第一段就搞了這麼久啊這麼久

utils

而後iScroll將本身下面會用到的經常使用操做封裝到了這個對象中——utils。

  1 var utils = (function () {
  2     var me = {};
  3 
  4     var _elementStyle = document.createElement('div').style;
  5     var _vendor = (function () {
  6         var vendors = ['t', 'webkitT', 'MozT', 'msT', 'OT'],
  7             transform,
  8             i = 0,
  9             l = vendors.length;
 10 
 11         for ( ; i < l; i++ ) {
 12             transform = vendors[i] + 'ransform';
 13             if ( transform in _elementStyle ) return vendors[i].substr(0, vendors[i].length-1);
 14         }
 15 
 16         return false;
 17     })();
 18 
 19     function _prefixStyle (style) {
 20         if ( _vendor === false ) return false;
 21         if ( _vendor === '' ) return style;
 22         return _vendor + style.charAt(0).toUpperCase() + style.substr(1);
 23     }
 24 
 25     me.getTime = Date.now || function getTime () { return new Date().getTime(); };
 26 
 27     me.extend = function (target, obj) {
 28         for ( var i in obj ) {
 29             target[i] = obj[i];
 30         }
 31     };
 32 
 33     me.addEvent = function (el, type, fn, capture) {
 34         el.addEventListener(type, fn, !!capture);
 35     };
 36 
 37     me.removeEvent = function (el, type, fn, capture) {
 38         el.removeEventListener(type, fn, !!capture);
 39     };
 40 
 41     me.momentum = function (current, start, time, lowerMargin, wrapperSize) {
 42         var distance = current - start,
 43             speed = Math.abs(distance) / time,
 44             destination,
 45             duration,
 46             deceleration = 0.0006;
 47 
 48         destination = current + ( speed * speed ) / ( 2 * deceleration ) * ( distance < 0 ? -1 : 1 );
 49         duration = speed / deceleration;
 50 
 51         if ( destination < lowerMargin ) {
 52             destination = wrapperSize ? lowerMargin - ( wrapperSize / 2.5 * ( speed / 8 ) ) : lowerMargin;
 53             distance = Math.abs(destination - current);
 54             duration = distance / speed;
 55         } else if ( destination > 0 ) {
 56             destination = wrapperSize ? wrapperSize / 2.5 * ( speed / 8 ) : 0;
 57             distance = Math.abs(current) + destination;
 58             duration = distance / speed;
 59         }
 60 
 61         return {
 62             destination: Math.round(destination),
 63             duration: duration
 64         };
 65     };
 66 
 67     var _transform = _prefixStyle('transform');
 68 
 69     me.extend(me, {
 70         hasTransform: _transform !== false,
 71         hasPerspective: _prefixStyle('perspective') in _elementStyle,
 72         hasTouch: 'ontouchstart' in window,
 73         hasPointer: navigator.msPointerEnabled,
 74         hasTransition: _prefixStyle('transition') in _elementStyle
 75     });
 76 
 77     // This should find all Android browsers lower than build 535.19 (both stock browser and webview)
 78     me.isBadAndroid = /Android/.test(window.navigator.appVersion) && !(/Chrome\/\d/.test(window.navigator.appVersion));
 79 
 80     me.extend(me.style = {}, {
 81         transform: _transform,
 82         transitionTimingFunction: _prefixStyle('transitionTimingFunction'),
 83         transitionDuration: _prefixStyle('transitionDuration'),
 84         transitionDelay: _prefixStyle('transitionDelay'),
 85         transformOrigin: _prefixStyle('transformOrigin')
 86     });
 87 
 88     me.hasClass = function (e, c) {
 89         var re = new RegExp("(^|\\s)" + c + "(\\s|$)");
 90         return re.test(e.className);
 91     };
 92 
 93     me.addClass = function (e, c) {
 94         if ( me.hasClass(e, c) ) {
 95             return;
 96         }
 97 
 98         var newclass = e.className.split(' ');
 99         newclass.push(c);
100         e.className = newclass.join(' ');
101     };
102 
103     me.removeClass = function (e, c) {
104         if ( !me.hasClass(e, c) ) {
105             return;
106         }
107 
108         var re = new RegExp("(^|\\s)" + c + "(\\s|$)", 'g');
109         e.className = e.className.replace(re, ' ');
110     };
111 
112     me.offset = function (el) {
113         var left = -el.offsetLeft,
114             top = -el.offsetTop;
115 
116         // jshint -W084
117         while (el = el.offsetParent) {
118             left -= el.offsetLeft;
119             top -= el.offsetTop;
120         }
121         // jshint +W084
122 
123         return {
124             left: left,
125             top: top
126         };
127     };
128 
129     me.preventDefaultException = function (el, exceptions) {
130         for ( var i in exceptions ) {
131             if ( exceptions[i].test(el[i]) ) {
132                 return true;
133             }
134         }
135 
136         return false;
137     };
138 
139     me.extend(me.eventType = {}, {
140         touchstart: 1,
141         touchmove: 1,
142         touchend: 1,
143 
144         mousedown: 2,
145         mousemove: 2,
146         mouseup: 2,
147 
148         MSPointerDown: 3,
149         MSPointerMove: 3,
150         MSPointerUp: 3
151     });
152 
153     me.extend(me.ease = {}, {
154         quadratic: {
155             style: 'cubic-bezier(0.25, 0.46, 0.45, 0.94)',
156             fn: function (k) {
157                 return k * ( 2 - k );
158             }
159         },
160         circular: {
161             style: 'cubic-bezier(0.1, 0.57, 0.1, 1)',    // Not properly "circular" but this looks better, it should be (0.075, 0.82, 0.165, 1)
162             fn: function (k) {
163                 return Math.sqrt( 1 - ( --k * k ) );
164             }
165         },
166         back: {
167             style: 'cubic-bezier(0.175, 0.885, 0.32, 1.275)',
168             fn: function (k) {
169                 var b = 4;
170                 return ( k = k - 1 ) * k * ( ( b + 1 ) * k + b ) + 1;
171             }
172         },
173         bounce: {
174             style: '',
175             fn: function (k) {
176                 if ( ( k /= 1 ) < ( 1 / 2.75 ) ) {
177                     return 7.5625 * k * k;
178                 } else if ( k < ( 2 / 2.75 ) ) {
179                     return 7.5625 * ( k -= ( 1.5 / 2.75 ) ) * k + 0.75;
180                 } else if ( k < ( 2.5 / 2.75 ) ) {
181                     return 7.5625 * ( k -= ( 2.25 / 2.75 ) ) * k + 0.9375;
182                 } else {
183                     return 7.5625 * ( k -= ( 2.625 / 2.75 ) ) * k + 0.984375;
184                 }
185             }
186         },
187         elastic: {
188             style: '',
189             fn: function (k) {
190                 var f = 0.22,
191                     e = 0.4;
192 
193                 if ( k === 0 ) { return 0; }
194                 if ( k == 1 ) { return 1; }
195 
196                 return ( e * Math.pow( 2, - 10 * k ) * Math.sin( ( k - f / 4 ) * ( 2 * Math.PI ) / f ) + 1 );
197             }
198         }
199     });
200 
201     me.tap = function (e, eventName) {
202         var ev = document.createEvent('Event');
203         ev.initEvent(eventName, true, true);
204         ev.pageX = e.pageX;
205         ev.pageY = e.pageY;
206         e.target.dispatchEvent(ev);
207     };
208 
209     me.click = function (e) {
210         var target = e.target,
211             ev;
212 
213         if (target.tagName != 'SELECT' && target.tagName != 'INPUT' && target.tagName != 'TEXTAREA') {
214             ev = document.createEvent('MouseEvents');
215             ev.initMouseEvent('click', true, true, e.view, 1,
216                 target.screenX, target.screenY, target.clientX, target.clientY,
217                 e.ctrlKey, e.altKey, e.shiftKey, e.metaKey,
218                 0, null);
219 
220             ev._constructed = true;
221             target.dispatchEvent(ev);
222         }
223     };
224 
225     return me;
226 })();
View Code

兼容性檢測

很煩的事情一而再再而三的在瀏覽器上面出現,好比咱們在chrome要定義動畫參數得加上一個前綴webkit,而後ff要使用MozT,這個事情很煩,因此iScroll這段代碼就在處理這個事情

 1 var _vendor = (function () {
 2     var vendors = ['t', 'webkitT', 'MozT', 'msT', 'OT'],
 3         transform,
 4         i = 0,
 5         l = vendors.length;
 6 
 7     for ( ; i < l; i++ ) {
 8         transform = vendors[i] + 'ransform';
 9         if ( transform in _elementStyle ) return vendors[i].substr(0, vendors[i].length-1);
10     }
11 
12     return false;
13 })();
14 
15 function _prefixStyle (style) {
16     if ( _vendor === false ) return false;
17     if ( _vendor === '' ) return style;
18     return _vendor + style.charAt(0).toUpperCase() + style.substr(1);
19 }

這裏作了幾個操做:

① 動態建立標籤樣式——_elementStyle

② 檢測樣式支持度,而且返回須要的前綴

③ 獲取驗證結果,好比在chrome下變會返回webkit-XXX

固然,這裏要加前綴的樣式,通常都與CSS3有關,而下面就會遇到是transform

getTime

me.getTime = Date.now || function getTime () { return new Date().getTime(); };
//獲取當前時間戳

extend

最最簡單的擴展對象的方法

1 me.extend = function (target, obj) {
2     for ( var i in obj ) {
3         target[i] = obj[i];
4     }
5 };

addEvent/removeEvent

事件註冊相關,我在想,如果使用了zepto這個代碼量會減小點麼?

1 me.addEvent = function (el, type, fn, capture) {
2     el.addEventListener(type, fn, !!capture);
3 };
4 
5 me.removeEvent = function (el, type, fn, capture) {
6     el.removeEventListener(type, fn, !!capture);
7 };

momentum

這個方法比較重要,用於計算動畫參數,會根據這個計算結果而決定動畫運動效果,其實咱們昨天的demo也用到了相似的東西

 1 me.momentum = function (current, start, time, lowerMargin, wrapperSize) {
 2     var distance = current - start,
 3         speed = Math.abs(distance) / time,
 4         destination,
 5         duration,
 6         deceleration = 0.0006;
 7 
 8     destination = current + ( speed * speed ) / ( 2 * deceleration ) * ( distance < 0 ? -1 : 1 );
 9     duration = speed / deceleration;
10 
11     if ( destination < lowerMargin ) {
12         destination = wrapperSize ? lowerMargin - ( wrapperSize / 2.5 * ( speed / 8 ) ) : lowerMargin;
13         distance = Math.abs(destination - current);
14         duration = distance / speed;
15     } else if ( destination > 0 ) {
16         destination = wrapperSize ? wrapperSize / 2.5 * ( speed / 8 ) : 0;
17         distance = Math.abs(current) + destination;
18         duration = distance / speed;
19     }
20 
21     return {
22         destination: Math.round(destination),
23         duration: duration
24     };
25 };

咱們來作一點解釋,首先看看咱們的參數:

① 這個方法應該是在touchend時候使用,第一個參數爲當前鼠標的位置

② 第二個參數是touchstart時候記錄的座標位置

③ 第三個參數爲時間參數,即是開始觸屏到離開時候所用時間(touchstart到touchend)

PS:這裏咱們其實能夠作一個猜想了,咱們有一次觸屏的時間與距離,天然能夠根據動力加速度計算出這次應該運動的時間與距離

④ 第四個參數是幹神馬的還不太明確,應該是控制邊界位置的,這個就決定了咱們不能無限制的拖動wrapper

⑤ 第五個參數爲容器的高度

而後咱們來以此讀一讀這裏的代碼:

① 得出這次拉動的距離distance/而後計算出此次拖動的速度(PS:我的以爲這裏操做很不錯,我沒有想到)

② 而後定義了一些其它參數,deceleration這個用於計算速度/距離的參數,而後兩個就是要返回的距離以及時間了

PS:我想說他這裏的計算最終位置的函數應該是物理裏面的一個計算摩擦參數的公式,尼瑪是什麼我真的不知道了,還有平方來着......

③ 這裏還有一個關鍵點就是distance有多是負值,這個會決定向上仍是向下運動

④ 通常狀況這裏就結束來了,而後下面if裏面一大段計算是用於處理運動軌跡超出時候的距離與速度從新計算(反彈效果)

好了,這個函數比較關鍵,他主要返回了最後要去到的位置,已經到這個位置的時間,裏面具體的實現咱們暫時不關係,後面這個必須理一理

檢測與初始化

接下來作了一大段能力檢測,好比:

① 是否支持CSS3動畫相關(transform、transition)

② 是否支持touch事件

而後作了一些簡單的初始化操做,這裏新增了一個style對象,爲他賦予了CSS動畫相關的屬性

③ 接下來是一些簡單的樣式操做,這樣有個函數須要注意,他能夠獲取一個元素真正的位置信息

 1 me.offset = function (el) {
 2     var left = -el.offsetLeft,
 3         top = -el.offsetTop;
 4 
 5     // jshint -W084
 6     while (el = el.offsetParent) {
 7         left -= el.offsetLeft;
 8         top -= el.offsetTop;
 9     }
10     // jshint +W084
11 
12     return {
13         left: left,
14         top: top
15     };
16 };

④ 動畫曲線

 1 me.extend(me.ease = {}, {
 2     quadratic: {
 3         style: 'cubic-bezier(0.25, 0.46, 0.45, 0.94)',
 4         fn: function (k) {
 5             return k * ( 2 - k );
 6         }
 7     },
 8     circular: {
 9         style: 'cubic-bezier(0.1, 0.57, 0.1, 1)',    // Not properly "circular" but this looks better, it should be (0.075, 0.82, 0.165, 1)
10         fn: function (k) {
11             return Math.sqrt( 1 - ( --k * k ) );
12         }
13     },
14     back: {
15         style: 'cubic-bezier(0.175, 0.885, 0.32, 1.275)',
16         fn: function (k) {
17             var b = 4;
18             return ( k = k - 1 ) * k * ( ( b + 1 ) * k + b ) + 1;
19         }
20     },
21     bounce: {
22         style: '',
23         fn: function (k) {
24             if ( ( k /= 1 ) < ( 1 / 2.75 ) ) {
25                 return 7.5625 * k * k;
26             } else if ( k < ( 2 / 2.75 ) ) {
27                 return 7.5625 * ( k -= ( 1.5 / 2.75 ) ) * k + 0.75;
28             } else if ( k < ( 2.5 / 2.75 ) ) {
29                 return 7.5625 * ( k -= ( 2.25 / 2.75 ) ) * k + 0.9375;
30             } else {
31                 return 7.5625 * ( k -= ( 2.625 / 2.75 ) ) * k + 0.984375;
32             }
33         }
34     },
35     elastic: {
36         style: '',
37         fn: function (k) {
38             var f = 0.22,
39                 e = 0.4;
40 
41             if ( k === 0 ) { return 0; }
42             if ( k == 1 ) { return 1; }
43 
44             return ( e * Math.pow( 2, - 10 * k ) * Math.sin( ( k - f / 4 ) * ( 2 * Math.PI ) / f ) + 1 );
45         }
46     }
47 });
View Code

這裏定義了動畫曲線供咱們選取,我通常使用linear......

PS:這個地方的代碼,不明覺厲!!!

自定義點擊事件

 1 me.tap = function (e, eventName) {
 2     var ev = document.createEvent('Event');
 3     ev.initEvent(eventName, true, true);
 4     ev.pageX = e.pageX;
 5     ev.pageY = e.pageY;
 6     e.target.dispatchEvent(ev);
 7 };
 8 
 9 me.click = function (e) {
10     var target = e.target,
11         ev;
12 
13     if (target.tagName != 'SELECT' && target.tagName != 'INPUT' && target.tagName != 'TEXTAREA') {
14         ev = document.createEvent('MouseEvents');
15         ev.initMouseEvent('click', true, true, e.view, 1,
16             target.screenX, target.screenY, target.clientX, target.clientY,
17             e.ctrlKey, e.altKey, e.shiftKey, e.metaKey,
18             0, null);
19 
20         ev._constructed = true;
21         target.dispatchEvent(ev);
22     }
23 };

iScroll這裏幹了一件壞事,本身定義了tap以及click,意思是他能夠觸發綁定到dom上的tap或者click事件

而後整個util便結束了,其中momentum方法非常關鍵,接下來咱們跟着程序流程走了

構造函數

構造函數是iScroll的入口,咱們來詳細讀一讀:

wrapper/scroller

爲咱們的外層結構,再裏面一點就是拖動元素了,iscroll的處理是認爲wrapper下第一個元素就是可拖動元素,我這裏任務不妥......

 

this.scroller = this.wrapper.children[0];

咱們拖動的就是這個scroller了,我爲何說這樣不妥呢?由於我若是如今又一個彈出層想使用iScroll的話,如果我彈出層有了wrapper了,我想本身往裏面裝DOM

而個人DOM搞很差有幾個兄弟節點,這個時候我確定不想本身再包裹一層的,因此iScroll這裏的scroller我以爲系統構建比較合理(固然這只是我的認爲)

下面還緩存了下當前scroll元素的style

this.scrollerStyle = this.scroller.style;    

初始化參數

iScroll固然本身會初始化一些默認屬性了:

 1 this.options = {
 2 
 3     resizeIndicator: true,
 4 
 5     mouseWheelSpeed: 20,
 6 
 7     snapThreshold: 0.334,
 8 
 9 // INSERT POINT: OPTIONS 
10 
11     startX: 0,
12     startY: 0,
13     scrollY: true,
14     directionLockThreshold: 5,
15     momentum: true,
16 
17     bounce: true,
18     bounceTime: 600,
19     bounceEasing: '',
20 
21     preventDefault: true,
22     preventDefaultException: { tagName: /^(INPUT|TEXTAREA|BUTTON|SELECT)$/ },
23 
24     HWCompositing: true,
25     useTransition: true,
26     useTransform: true
27 };

如果咱們傳了相關屬性會被複寫的(這裏上面定義了extend,卻沒有使用):

1 for ( var i in options ) {
2     this.options[i] = options[i];
3 }

能力檢測

而後下面一大片結果基本作了一些能力檢測的功能,而後將檢測結果保存,由於我暫時只關心縱向滑動,因此一些地方便不予關心了

1 this.x = 0;
2 this.y = 0;
3 this.directionX = 0;
4 this.directionY = 0;
5 this._events = {};

這一坨東西仍是要關注的,方向和初始值

初始化_init

上面一些默認屬性定義結束便進入真正的初始化階段,

 1 _init: function () {
 2     this._initEvents();
 3 
 4     if ( this.options.scrollbars || this.options.indicators ) {
 5         this._initIndicators();
 6     }
 7 
 8     if ( this.options.mouseWheel ) {
 9         this._initWheel();
10     }
11 
12     if ( this.options.snap ) {
13         this._initSnap();
14     }
15 
16     if ( this.options.keyBindings ) {
17         this._initKeys();
18     }
19 // INSERT POINT: _init
20 },

代碼很清晰,我如今的需求關注_initEvents與_initIndicators就行了,其它暫時能夠無論,關鍵點即是事件綁定了

 

_initEvents

 1 _initEvents: function (remove) {
 2     var eventType = remove ? utils.removeEvent : utils.addEvent,
 3         target = this.options.bindToWrapper ? this.wrapper : window;
 4 
 5     eventType(window, 'orientationchange', this);
 6     eventType(window, 'resize', this);
 7 
 8     if ( this.options.click ) {
 9         eventType(this.wrapper, 'click', this, true);
10     }
11 
12     if ( !this.options.disableMouse ) {
13         eventType(this.wrapper, 'mousedown', this);
14         eventType(target, 'mousemove', this);
15         eventType(target, 'mousecancel', this);
16         eventType(target, 'mouseup', this);
17     }
18 
19     if ( utils.hasPointer && !this.options.disablePointer ) {
20         eventType(this.wrapper, 'MSPointerDown', this);
21         eventType(target, 'MSPointerMove', this);
22         eventType(target, 'MSPointerCancel', this);
23         eventType(target, 'MSPointerUp', this);
24     }
25 
26     if ( utils.hasTouch && !this.options.disableTouch ) {
27         eventType(this.wrapper, 'touchstart', this);
28         eventType(target, 'touchmove', this);
29         eventType(target, 'touchcancel', this);
30         eventType(target, 'touchend', this);
31     }
32 
33     eventType(this.scroller, 'transitionend', this);
34     eventType(this.scroller, 'webkitTransitionEnd', this);
35     eventType(this.scroller, 'oTransitionEnd', this);
36     eventType(this.scroller, 'MSTransitionEnd', this);
37 },

這段代碼,是整個iScroll的核心,整個入口函數其實在這裏,咱們暫時的關注點又在這裏:

1 if ( utils.hasTouch && !this.options.disableTouch ) {
2     eventType(this.wrapper, 'touchstart', this);
3     eventType(target, 'touchmove', this);
4     eventType(target, 'touchcancel', this);
5     eventType(target, 'touchend', this);
6 }

PS:這裏有一點讓我比較疑惑的就是這裏傳遞進去的fn是一個對象,而不是函數,看來我事件機制一塊仍然不到家:

而後進入咱們的touch事件,反正如今touchstart便會進入咱們的start回調函數

touchStart

 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 
 9     if ( !this.enabled || (this.initiated && utils.eventType[e.type] !== this.initiated) ) {
10         return;
11     }
12 
13     if ( this.options.preventDefault && !utils.isBadAndroid && !utils.preventDefaultException(e.target, this.options.preventDefaultException) ) {
14         e.preventDefault();
15     }
16 
17     var point = e.touches ? e.touches[0] : e,
18         pos;
19 
20     this.initiated    = utils.eventType[e.type];
21     this.moved        = false;
22     this.distX        = 0;
23     this.distY        = 0;
24     this.directionX = 0;
25     this.directionY = 0;
26     this.directionLocked = 0;
27 
28     this._transitionTime();
29 
30     this.startTime = utils.getTime();
31 
32     if ( this.options.useTransition && this.isInTransition ) {
33         this.isInTransition = false;
34         pos = this.getComputedPosition();
35         this._translate(Math.round(pos.x), Math.round(pos.y));
36         this._execEvent('scrollEnd');
37     } else if ( !this.options.useTransition && this.isAnimating ) {
38         this.isAnimating = false;
39         this._execEvent('scrollEnd');
40     }
41 
42     this.startX    = this.x;
43     this.startY    = this.y;
44     this.absStartX = this.x;
45     this.absStartY = this.y;
46     this.pointX    = point.pageX;
47     this.pointY    = point.pageY;
48 
49     this._execEvent('beforeScrollStart');
50 }

前面作了一系列的兼容性處理,而後記錄了一些數據便結束了

結語

今天有點晚了,我也暫時結束了,明天還要上班呢,下次詳細研究下touch事件的幾個階段乾的事情以及滾動條的實現

相關文章
相關標籤/搜索