咱們昨天初步瞭解了爲何會出現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
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>
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還大
並且整個庫的註釋寫的很差,好像壓根就沒寫......不知道閱讀上會不會有障礙,因而咱們進入源碼
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實現坦克大戰的朋友就會有所體會了
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第一段就搞了這麼久啊這麼久
而後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 })();
很煩的事情一而再再而三的在瀏覽器上面出現,好比咱們在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
me.getTime = Date.now || function getTime () { return new Date().getTime(); }; //獲取當前時間戳
最最簡單的擴展對象的方法
1 me.extend = function (target, obj) { 2 for ( var i in obj ) { 3 target[i] = obj[i]; 4 } 5 };
事件註冊相關,我在想,如果使用了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 };
這個方法比較重要,用於計算動畫參數,會根據這個計算結果而決定動畫運動效果,其實咱們昨天的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 });
這裏定義了動畫曲線供咱們選取,我通常使用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的入口,咱們來詳細讀一讀:
爲咱們的外層結構,再裏面一點就是拖動元素了,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 = {};
這一坨東西仍是要關注的,方向和初始值
上面一些默認屬性定義結束便進入真正的初始化階段,
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就行了,其它暫時能夠無論,關鍵點即是事件綁定了
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回調函數
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事件的幾個階段乾的事情以及滾動條的實現