相信對移動端有了解的朋友對iScroll這個庫很是熟悉吧,今天咱們就來講下咱們移動頁面的iScroll化javascript
iScroll是咱們必學框架之一,咱們此次先根據iScroll功能本身實現其功能,而後再學習iScroll源碼css
下面先給出iScroll官方的例子和源碼,要看效果的朋友本身去看吧:https://github.com/cubiq/iscrollhtml
本人能力有限,文中有誤請提出html5
在移動端新出了一個屬性叫作「viewport」,這個即是咱們手機上的虛擬視口(viewport),也就是視覺窗口,顯示區域java
移動設備的顯示區域比電腦小得多(但也方便得多),爲了讓手機顯示的更加友好,Apple提供了一個方法:在瀏覽器定義了viewport meta標籤node
他的做用就是建立一個虛擬窗口,這個虛擬窗口接近桌面瀏覽器(980px),事實上viewport就是用以放大縮小網頁內容android
<meta name=」viewport」
content=」width=device-width, initial-scale=1, maximum-scale=1″>
width:控制 viewport 的大小,能夠指定的一個值,若是 600,或者特殊的值,如 device-width 爲設備的寬度(單位爲縮放爲 100% 時的 CSS 的像素)。
height:和 width 相對應,指定高度。
initial-scale:初始縮放比例,也便是當頁面第一次 load 的時候縮放比例。
maximum-scale:容許用戶縮放到的最大比例。
minimum-scale:容許用戶縮放到的最小比例。
user-scalable:用戶是否能夠手動縮放
(此處引用——原文出處: quirksmode 譯文出處: Zhao Yuhao)ios
想象咱們有個房間,咱們能夠控制房間大小,如今咱們站在他窗戶面前,正對着窗戶的牆壁塗滿了壁畫,咱們走到窗口一米的位置往房間看(假設房子很大)git
咱們能看到整個壁畫,可是有點小,因而咱們縮小房子就能看清細節了,這裏的窗戶就是visual viewport 牆壁就是layout viewportgithub
對於css佈局,特別是用寬度百分比作排版時候,比率是按照layout viewport計算,也就是說一個div相對寬度50%,用戶在手機瀏覽器放大縮小
div寬度不會一直顯示相對窗口50%,整個div可能鋪滿窗口小到看不到
咱們這裏的viewport就至關於放大和縮小房間,找到一個合適的平衡點,讓咱們的網頁在手機上更友好的顯示
① 假如咱們如今又個簡單的頁面,不給div設置寬度(默認是layout100%——980px),因此顯示效果爲:
② 用戶經過放大網頁比例,縮小visual viewport的值,相對而言用戶就能看清楚div的內容了,可是layout viewport自己未發現變化(因此可能出現滾動條)
③ 這個時候上文中的device-width就派上了用場,他能夠將layout viewport的像素設置爲設備的像素,這樣的話:
visual viewport=layout viewport=screen width,這個體驗就比較好了
以上知識點暫時到這,這裏咱們補充幾個知識點:
① 寬度問題:
layout viewport 的長寬 (document.documentElement.clientWidth / document.documentElement.clientHeight)
visual viewport 的長寬 (window.innerWidth / window.innerHeight)
② 設備像素
screen.width/height
③ Media queries,這個是html5新增特性,能夠根據device-width(設備寬度,screen width)來肯定顯示不一樣的CSS
1. visual viewport 寬度 : 默認980 實際大小與縮放比例相關,能夠經過meta的viewport屬性修改
2. layout viewport 寬度 : 980
3. screen.width :320
咱們這裏來從新理解下device-width這個屬性,這裏提供一段代碼,兩個截圖:
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 <meta name="viewport" content="width=device-width"> 6 </head> 7 <body> 8 方法是多少的發生的范德薩方法是多少的發生的范德薩方法是多少的發生的范德薩方法是多少的發生的范德薩方法是多少的發生的范德薩方法是多少的發生的范德薩方法是多少的發生的范德薩方法是多少的發生的范德薩方法是多少的發生的范德薩方法是多少的發生的范德薩方法是多少的發生的范德薩方法是多少的發生的范德薩方法是多少的發生的范德薩方法是多少的發生的范德薩方法是多少的發生的范德薩方法是多少的發生的范德薩方法是多少的發生的范德薩方法是多少的發生的范德薩方法是多少的發生的范德薩方法是多少的發生的范德薩方法是多少的發生的范德薩方法是多少的發生的范德薩方法是多少的發生的范德薩方法是多少的發生的范德薩方法是多少的發生的范德薩方法是多少的發生的范德薩方法是多少的發生的范德薩方法是多少的發生的范德薩方法是多少的發生的范德薩方法是多少的發生的范德薩方法是多少的發生的范德薩方法是多少的發生的范德薩方法是多少的發生的范德薩方法是多少的發生的范德薩方法是多少的發生的范德薩方法是多少的發生的范德薩方法是多少的發生的范德薩方法是多少的發生的范德薩方法是多少的發生的范德薩方法是多少的發生的范德薩方法是多少的發生的范德薩方法是多少的發生的范德薩方法是多少的發生的范德薩方法是多少的發生的范德薩方法是多少的發生的范德薩方法是多少的發生的范德薩方法是多少的發生的范德薩方法是多少的發生的范德薩方法是多少的發生的范德薩方法是多少的發生的范德薩方法是多少的發生的范德薩方法是多少的發生的范德薩方法是多少的發生的范德薩方法是多少的發生的范德薩方法是多少的發生的范德薩方法是多少的發生的范德薩方法是多少的發生的范德薩方法是多少的發生的范德薩方法是多少的發生的范德薩方法是多少的發生的范德薩方法是多少的發生的范德薩方法是多少的發生的范德薩方法是多少的發生的范德薩方法是多少的發生的范德薩 9 </body> 10 </html>
<meta name="viewport" content="width=100"> <!-- 這裏以此給width賦值:① 默認狀況/② device-width/③ 100 ->
最初的ios與android並不支持fixed屬性,由於咱們的手機有一個叫viewport的東西這個你們都知道了,fixed位置是相對整個頁面的固定位置
頁面中的頁面沒什麼變化,只不過在viewport下變大了,並且咱們移動的是viewport,網頁並未跟着滾動,因而咱們移動的事實上是viewport,
而咱們viewport移動並不會讓咱們fixed元素跟着變化,由於他是相對於手機屏幕的,因此就不支持了,反正後面這個問題被修復了
可是據個人經驗來講,就是如今ios六、7或者android高版本fixed仍然不是那麼好使,移動端的fixed就跟ie7的float似的,讓人想哭
特別是當你點擊文本框時候看到鍵盤上來了,頁面錯位了,一股想扔手機之情油然而生
加之想要用戶自動升級手機瀏覽器什麼的仍然不現實,因此iScroll誕生了,這是iScroll誕生的主要緣由(我是這麼認爲的)
既然fixed很差使,那麼就頭尾固定,中間body部分使用overflow屬性吧,可是可恨的是overflow屬性仍然很差使!!!
咱們這裏來作一個demo:
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 <meta name="viewport" content="width=device-width,initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no"> 6 <style type="text/css"> 7 #header { width: 100%; height: 50px; position: absolute; top: 0; left: 0; } 8 #footer { width: 100%; height: 50px; position: absolute; bottom: 0; left: 0; } 9 #body { height: 180px; margin: 60px 0; overflow: auto; } 10 div { border: 1px solid black; } 11 </style> 12 </head> 13 <body> 14 <div id="header">header</div> 15 <div id="body">方法是多少的發生的范德薩方法是多少的發生的范德薩方法是多少的發生的范德薩方法是多少的發生的范德薩方法是多少的發生的范德薩方法是多少的發生的范德薩方法是多少的發生的范德薩方法是多少的發生的范德薩方法是多少的發生的范德薩方法是多少的發生的范德薩方法是多少的發生的范德薩方法是多少的發生的范德薩方法是多少的發生的范德薩方法是多少的發生的范德薩方法是多少的發生的范德薩方法是多少的發生的范德薩方法是多少的發生的范德薩方法是多少的發生的范德薩方法是多少的發生的范德薩方法是多少的發生的范德薩方法是多少的發生的范德薩方法是多少的發生的范德薩方法是多少的發生的范德薩方法是多少的發生的范德薩方法是多少的發生的范德薩方法是多少的發生的范德薩方法是多少的發生的范德薩方法是多少的發生的范德薩方法是多少的發生的范德薩方法是多少的發生的范德薩方法是多少的發生的范德薩方法是多少的發生的范德薩方法是多少的發生的范德薩方法是多少的發生的范德薩方法是多少的發生的范德薩方 16 <input type="text" /> 17 法是多少的發生的范德薩方法是多少的發生的范德薩方法是多少的發生的范德薩方法是多少的發生的范德薩方法是多少的發生的范德薩方法是多少的發生的范德薩方法是多少的發生的范德薩方法是多少的發生的范德薩方法是多少的發生的范德薩方法是多少的發生的范德薩方法是多少的發生的范德薩方法是多少的發生的范德薩方法是多少的發生的范德薩方法是多少的發生的范德薩方法是多少的發生的范德薩方法是多少的發生的范德薩方法是多少的發生的范德薩方法是多少的發生的范德薩方法是多少的發生的范德薩方法是多少的發生的范德薩方法是多少的發生的范德薩方法是多少的發生的范德薩方法是多少的發生的范德薩方法是多少的發生的范德薩方法是多少的發生的范德薩方法是多少的發生的范德薩方法是多少的發生的范德薩方法是多少的發生的范德薩方法是多少的發生的范德薩 18 19 方法是多少的發生的范德薩方法是多少的發生的范德薩方法是多少的發生的范德薩方法是多少的發生的范德薩方法是多少的發生的范德薩方法是多少的發生的范德薩方法是多少的發生的范德薩方法是多少的發生的范德薩方法是多少的發生的范德薩方法是多少的發生的范德薩方法是多少的發生的范德薩方法是多少的發生的范德薩方法是多少的發生的范德薩方法是多少的發生的范德薩方法是多少的發生的范德薩方法是多少的發生的范德薩方法是多少的發生的范德薩方法是多少的發生的范德薩方法是多少的發生的范德薩方法是多少的發生的范德薩方法是多少的發生的范德薩方法是多少的發生的范德薩方法是多少的發生的范德薩方法是多少的發生的范德薩方法是多少的發生的范德薩方法是多少的發生的范德薩方法是多少的發生的范德薩方法是多少的發生的范德薩方法是多少的發生的范德薩方法是多少的發生的范德薩方法是多少的發生的范德薩方法是多少的發生的范德薩方法是多少的發生的范德薩方法是多少的發生的范德薩方法是多少的發生的范德薩方 20 <input type="text" /> 21 法是多少的發生的范德薩方法是多少的發生的范德薩方法是多少的發生的范德薩方法是多少的發生的范德薩方法是多少的發生的范德薩方法是多少的發生的范德薩方法是多少的發生的范德薩方法是多少的發生的范德薩方法是多少的發生的范德薩方法是多少的發生的范德薩方法是多少的發生的范德薩方法是多少的發生的范德薩方法是多少的發生的范德薩方法是多少的發生的范德薩方法是多少的發生的范德薩方法是多少的發生的范德薩方法是多少的發生的范德薩方法是多少的發生的范德薩方法是多少的發生的范德薩方法是多少的發生的范德薩方法是多少的發生的范德薩方法是多少的發生的范德薩方法是多少的發生的范德薩方法是多少的發生的范德薩方法是多少的發生的范德薩方法是多少的發生的范德薩方法是多少的發生的范德薩方法是多少的發生的范德薩方法是多少的發生的范德薩 22 23 </div> 24 <div id="footer">footer</div> 25 26 </body> 27 </html>
其實這個屬性也是能夠實現三行佈局的基本功能的,可是有如下幾個問題:
① 沒有滾動條
② 滾動不暢
③ 手機瀏覽器支持參差不齊
可是這些缺點也不能掩蓋他最大的優勢:原生性!!!原生的就是最好的,若是哪天這個屬性升級的話,前途就行了
要假裝APP,頁面切換動畫必不可少,可是若是中間部分不固定的話就會碰到另一個使人頭疼的問題:
長短頁切換問題,想象下幾個長短不一的頁面切換會有多醜呢???
PS:這也是我如今遇到的問題
基於以上緣由,因此出現了iScroll這樣的實用庫,固然,以上只是我的猜測......
以上扯了那麼多,與本文的最初目的關係其實不大,咱們的主要目的仍是得先實現一個簡單的iScroll功能才行(依賴zepto)
1 <html xmlns="http://www.w3.org/1999/xhtml"> 2 <head> 3 <title></title> 4 <meta name="viewport" content="width=device-width,initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no"> 5 <style type="text/css"> 6 body, div, dl, dt, dd, ul, ol, li, h1, h2, h3, h4, h5, h6, pre, code, form, fieldset, legend, input, textarea, p, blockquote, th, td, hr, button, article, aside, details, figcaption, figure, footer, header, hgroup, menu, nav, section { margin: 0; padding: 0; } 7 body { font: normal 14px/1.5 "Arial" , "Lucida Grande" ,Verdana, "Microsoft YaHei" , "hei"; -webkit-font-smoothing: antialiased; color: #000; background: #f5f5f5; } 8 header { position: absolute; top: 0; left: 0; width: 100%; height: 48px; background-color: #1491c5; } 9 footer { position: absolute; bottom: 0; left: 0; width: 100%; height: 48px; background-color: #1491c5; } 10 h1 { display: block; font-size: 2em; font-weight: bold; font-weight: 500; text-align: center; color: White; } 11 #body { background: #fff; border: 1px solid #cfcfcf; width: 96%; height: 100%; margin: 50px auto; padding: 4px; } 12 </style> 13 </head> 14 <body> 15 <header id="header"> 16 <h1> 17 Header</h1> 18 </header> 19 <div id="body"> 20 body 21 </div> 22 <footer> 23 <h1> 24 Footer</h1> 25 </footer> 26 </body> 27 </html>
咱們根據iScroll的動做先寫下如下代碼:
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 <meta name="viewport" content="width=device-width,initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no"> 6 <style type="text/css"> 7 body, div, dl, dt, dd, ul, ol, li, h1, h2, h3, h4, h5, h6, pre, code, form, fieldset, legend, input, textarea, p, blockquote, th, td, hr, button, article, aside, details, figcaption, figure, footer, header, hgroup, menu, nav, section { margin: 0; padding: 0; } 8 body { font: normal 14px/1.5 "Arial" , "Lucida Grande" ,Verdana, "Microsoft YaHei" , "hei"; -webkit-font-smoothing: antialiased; color: #000; background: #ccc; } 9 header { position: absolute; top: 0; left: 0; width: 100%; height: 48px; background-color: #1491c5; } 10 footer { position: absolute; bottom: 0; left: 0; width: 100%; height: 48px; background-color: #1491c5; } 11 h1 { display: block; font-size: 2em; font-weight: bold; font-weight: 500; text-align: center; color: White; } 12 13 #body { position: absolute; top: 50px; bottom: 50px; background: #fff; width: 100%; overflow: hidden; } 14 #wrapper { width: 100%; } 15 16 17 #wrapper li { padding: 0 10px; height: 40px; line-height: 40px; border-bottom: 1px solid #ccc; border-top: 1px solid #fff; background-color: #fafafa; font-size: 14px; } 18 </style> 19 </head> 20 <body> 21 <header id="header"> 22 <h1> 23 Header</h1> 24 </header> 25 <div id="body"> 26 <div id="wrapper"> 27 body 28 <ul> 29 <li>Pretty row 1</li> 30 <li>Pretty row 2</li> 31 <li>Pretty row 3</li> 32 <li>Pretty row 4</li> 33 <li> 34 <input type="text"></li> 35 <li>Pretty row 6</li> 36 <li>Pretty row 7</li> 37 <li>Pretty row 8</li> 38 <li> 39 <input type="checkbox"></li> 40 <li>Pretty row 10</li> 41 <li>Pretty row 11</li> 42 <li>Pretty row 12</li> 43 <li> 44 <input type="radio"></li> 45 <li>Pretty row 14</li> 46 <li>Pretty row 15</li> 47 <li>Pretty row 16</li> 48 <li> 49 <textarea></textarea></li> 50 <li>Pretty row 18</li> 51 <li>Pretty row 19</li> 52 <li>Pretty row 20</li> 53 <li> 54 <select> 55 <option>option</option> 56 </select></li> 57 <li>Pretty row 22</li> 58 <li>Pretty row 23</li> 59 <li>Pretty row 24</li> 60 </ul> 61 <hr /> 62 <ul> 63 <li>Pretty row 25</li> 64 <li>Pretty row 26</li> 65 <li>Pretty row 27</li> 66 <li>Pretty row 28</li> 67 <li>Pretty row 29</li> 68 <li>Pretty row 30</li> 69 <li>Pretty row 31</li> 70 <li>Pretty row 32</li> 71 <li>Pretty row 33</li> 72 <li>Pretty row 34</li> 73 <li>Pretty row 35</li> 74 <li>Pretty row 36</li> 75 <li>Pretty row 37</li> 76 <li>Pretty row 38</li> 77 <li>Pretty row 39</li> 78 <li>Pretty row 40</li> 79 <li>Pretty row 41</li> 80 <li>Pretty row 42</li> 81 <li>Pretty row 43</li> 82 <li>Pretty row 44</li> 83 <li>Pretty row 45</li> 84 <li>Pretty row 46</li> 85 <li>Pretty row 47</li> 86 <li>Pretty row 48</li> 87 <li>Pretty row 49</li> 88 <li>Pretty row 50</li> 89 </ul> 90 </div> 91 </div> 92 <footer> 93 <h1> 94 Footer</h1> 95 </footer> 96 <script src="../../zepto/zepto-1.0/src/zepto.js" type="text/javascript"></script> 97 <script type="text/javascript"> 98 var Scroll = function (opts) { 99 opts = opts || {}; 100 //檢測設備事件支持,肯定使用鼠標事件或者touch事件 101 this._checkEventCompatibility(); 102 this._setBaseParam(opts); 103 this._initScrollBar(); 104 // this._addEvent(); 105 }; 106 107 Scroll.prototype = { 108 constructor: Scroll, 109 //檢測設備事件兼容 110 _checkEventCompatibility: function () { 111 var isTouch = 'ontouchstart' in document.documentElement; 112 this.start = isTouch ? 'touchstart' : 'mousedown'; 113 this.move = isTouch ? 'touchmove' : 'mousemove'; 114 this.end = isTouch ? 'touchend' : 'mouseup'; 115 this.startFn; 116 this.moveFn; 117 this.endFn; 118 }, 119 //基本參數設置 120 _setBaseParam: function (opts) { 121 this.timeGap = 0; //時間間隔 122 this.touchTime = 0; //開始時間 123 this.isMoveing = false; //是否正在移動 124 this.moveState = 'up'; //移動狀態,up right down left 125 this.oTop = 0; //拖動前的top值 126 this.curTop = 0; //當前容器top 127 this.mouseY = 0; //鼠標第一次點下時相對父容器的位置 128 this.animateParam = opts.animateParam || [10, 8, 6, 5, 4, 3, 2, 1, 0, 0, 0]; //動畫參數 129 this.cooling = true; //是否處於冷卻時間 130 this.steplen = 25; //動畫步長 131 132 this.wrapper = opts.wrapper || $('body'); 133 this.dragEl = opts.body; 134 this.wrapper.css({ 'position': 'absolute', 'overflow': 'hidden' }); 135 this.dragEl.css('position', 'absolute'); 136 this.wrapper.append(this.dragEl); 137 }, 138 _initScrollBar: function () { 139 if (!this.dragHeight) { 140 this.dragHeight = this.dragEl.offset().height; //拖動元素高度 141 this.wrapperHeight = this.wrapper.offset().height; 142 } 143 //滾動條縮放比例 144 this.scrollProportion = this.wrapperHeight / this.dragHeight; 145 this.isNeedScrollBar = true; 146 //該種狀況無需滾動條 147 if (this.scrollProportion >= 1) { 148 this.isNeedScrollBar = false; ; 149 return false; 150 } 151 //滾動條 152 this.scrollBar = $('<div style="background-color: rgba(0, 0, 0, 0.498039);border: 1px solid rgba(255, 255, 255, 0.901961); width: 5px; border-radius: 3px; position: absolute; right: 1px; opacity: 0.2; "></div>'); 153 this.wrapper.append(this.scrollBar); 154 this.scrollHeight = parseInt(this.scrollProportion * this.wrapperHeight); 155 this.scrollBar.css('height', this.scrollHeight); 156 } 157 158 }; 159 new Scroll({ wrapper: $('#body'), body: $('#wrapper') }); 160 </script> 161 </body> 162 </html>
http://sandbox.runjs.cn/show/oztjkadg(最好使用手機訪問)
樣式出來了,咱們如今就該註冊事件了,支持touch就是要touch,不然就是要mouse事件了:
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 <meta name="viewport" content="width=device-width,initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no"> 6 <style type="text/css"> 7 body, div, dl, dt, dd, ul, ol, li, h1, h2, h3, h4, h5, h6, pre, code, form, fieldset, legend, input, textarea, p, blockquote, th, td, hr, button, article, aside, details, figcaption, figure, footer, header, hgroup, menu, nav, section { margin: 0; padding: 0; } 8 body { font: normal 14px/1.5 "Arial" , "Lucida Grande" ,Verdana, "Microsoft YaHei" , "hei"; -webkit-font-smoothing: antialiased; color: #000; background: #ccc; } 9 header { position: absolute; top: 0; left: 0; width: 100%; height: 48px; background-color: #1491c5; } 10 footer { position: absolute; bottom: 0; left: 0; width: 100%; height: 48px; background-color: #1491c5; } 11 h1 { display: block; font-size: 2em; font-weight: bold; font-weight: 500; text-align: center; color: White; } 12 13 #body { position: absolute; top: 50px; bottom: 50px; background: #fff; width: 100%; overflow: hidden; } 14 #wrapper { width: 100%; } 15 16 17 #wrapper li { padding: 0 10px; height: 40px; line-height: 40px; border-bottom: 1px solid #ccc; border-top: 1px solid #fff; background-color: #fafafa; font-size: 14px; } 18 </style> 19 <script id="others_zepto_10rc1" type="text/javascript" class="library" src="/js/sandbox/other/zepto.min.js"></script> 20 </head> 21 <body> 22 <header id="header"> 23 <h1> 24 Header</h1> 25 </header> 26 <div id="body"> 27 <div id="wrapper"> 28 body 29 <ul> 30 <li>Pretty row 1</li> 31 <li>Pretty row 2</li> 32 <li>Pretty row 3</li> 33 <li>Pretty row 4</li> 34 <li> 35 <input type="text"></li> 36 <li>Pretty row 6</li> 37 <li>Pretty row 7</li> 38 <li>Pretty row 8</li> 39 <li> 40 <input type="checkbox"></li> 41 <li>Pretty row 10</li> 42 <li>Pretty row 11</li> 43 <li>Pretty row 12</li> 44 <li> 45 <input type="radio"></li> 46 <li>Pretty row 14</li> 47 <li>Pretty row 15</li> 48 <li>Pretty row 16</li> 49 <li> 50 <textarea></textarea></li> 51 <li>Pretty row 18</li> 52 <li>Pretty row 19</li> 53 <li>Pretty row 20</li> 54 <li> 55 <select> 56 <option>option</option> 57 </select></li> 58 </ul> 59 <hr /> 60 <ul> 61 62 <li>Pretty row 41</li> 63 <li>Pretty row 42</li> 64 <li>Pretty row 43</li> 65 <li>Pretty row 44</li> 66 <li>Pretty row 45</li> 67 <li>Pretty row 46</li> 68 <li>Pretty row 47</li> 69 <li>Pretty row 48</li> 70 <li>Pretty row 49</li> 71 <li>Pretty row 50</li> 72 </ul> 73 </div> 74 </div> 75 <footer> 76 <h1> 77 Footer</h1> 78 </footer> 79 <script src="zepto.js" type="text/javascript"></script> 80 <script type="text/javascript"> 81 var Scroll = function (opts) { 82 opts = opts || {}; 83 //檢測設備事件支持,肯定使用鼠標事件或者touch事件 84 this._checkEventCompatibility(); 85 this._setBaseParam(opts); 86 this._addEvent(); 87 88 this._initScrollBar(); 89 }; 90 91 Scroll.prototype = { 92 constructor: Scroll, 93 //檢測設備事件兼容 94 _checkEventCompatibility: function () { 95 var isTouch = 'ontouchstart' in document.documentElement; 96 97 this.start = isTouch ? 'touchstart' : 'mousedown'; 98 this.move = isTouch ? 'touchmove' : 'mousemove'; 99 this.end = isTouch ? 'touchend' : 'mouseup'; 100 this.startFn; 101 this.moveFn; 102 this.endFn; 103 }, 104 //基本參數設置 105 _setBaseParam: function (opts) { 106 this.timeGap = 0; //時間間隔 107 this.touchTime = 0; //開始時間 108 this.isMoveing = false; //是否正在移動 109 this.moveState = 'up'; //移動狀態,up right down left 110 this.oTop = 0; //拖動前的top值 111 this.curTop = 0; //當前容器top 112 this.mouseY = 0; //鼠標第一次點下時相對父容器的位置 113 this.animateParam = opts.animateParam || [10, 8, 6, 5, 4, 3, 2, 1, 0, 0, 0]; //動畫參數 114 this.cooling = true; //是否處於冷卻時間 115 this.steplen = 25; //動畫步長 116 117 this.wrapper = opts.wrapper || $('body'); 118 this.dragEl = opts.body; 119 this.wrapper.css({ 'position': 'absolute', 'overflow': 'hidden' }); 120 this.dragEl.css('position', 'absolute'); 121 this.wrapper.append(this.dragEl); 122 }, 123 _initScrollBar: function () { 124 if (!this.dragHeight) { 125 this.dragHeight = this.dragEl.offset().height; //拖動元素高度 126 this.wrapperHeight = this.wrapper.offset().height; 127 } 128 //滾動條縮放比例 129 this.scrollProportion = this.wrapperHeight / this.dragHeight; 130 this.isNeedScrollBar = true; 131 //該種狀況無需滾動條 132 if (this.scrollProportion >= 1) { 133 this.isNeedScrollBar = false; ; 134 return false; 135 } 136 //滾動條 137 this.scrollBar = $('<div style="background-color: rgba(0, 0, 0, 0.498039);border: 1px solid rgba(255, 255, 255, 0.901961); width: 5px; border-radius: 3px; position: absolute; right: 1px; opacity: 0.2; "></div>'); 138 this.wrapper.append(this.scrollBar); 139 this.scrollHeight = parseInt(this.scrollProportion * this.wrapperHeight); 140 this.scrollBar.css('height', this.scrollHeight); 141 }, 142 _setScrollTop: function (top, duration) { 143 //滾動條高度 144 if (this.isNeedScrollBar) { 145 top = this._getResetData(top).sTop; 146 top = top < 0 ? (top + 10) : top; 147 148 var scrollTop = top * (-1); 149 if (typeof duration == 'number') { 150 var _top = parseInt(scrollTop * this.scrollProportion) + 'px'; 151 this.scrollBar.animate({ 152 top: _top, 153 right: '1px' 154 }, duration, 'linear'); 155 156 } else { 157 this.scrollBar.css('top', parseInt(scrollTop * this.scrollProportion) + 'px'); 158 } 159 this.scrollBar.css('opacity', '0.8'); 160 } 161 }, 162 _hideScroll: function () { 163 if (this.isNeedScrollBar) { 164 this.scrollBar.animate({ 'opacity': '0.2' }); 165 } 166 }, 167 _addEvent: function () { 168 var scope = this; 169 this.startFn = function (e) { 170 scope._touchStart.call(scope, e); 171 }; 172 this.moveFn = function (e) { 173 scope._touchMove.call(scope, e); 174 }; 175 this.endFn = function (e) { 176 scope._touchEnd.call(scope, e); 177 }; 178 this.dragEl[0].addEventListener(this.start, this.startFn, false); 179 document.addEventListener(this.move, this.moveFn, false); 180 document.addEventListener(this.end, this.endFn, false); 181 }, 182 removeEvent: function () { 183 this.dragEl[0].removeEventListener(this.start, this.startFn); 184 document.removeEventListener(this.move, this.moveFn); 185 document.removeEventListener(this.end, this.endFn); 186 }, 187 _touchStart: function (e) { 188 var scope = this; 189 if (this.isMoveing) { e.preventDefault(); return false; } 190 //非運動狀況關閉冷卻時間 191 this.cooling = false; 192 this.touchTime = e.timeStamp; 193 pos = this.getMousePos((e.changedTouches && e.changedTouches[0]) || e); 194 var top = parseFloat(this.dragEl.css('top')) || 0; 195 this.mouseY = pos.top - top; 196 }, 197 _touchMove: function (e) { 198 if (this.cooling) { e.preventDefault(); return false; } 199 200 this.isMoveing = true; 201 202 e.preventDefault(); 203 var pos = this.getMousePos((e.changedTouches && e.changedTouches[0]) || e); 204 205 //防止點擊時候跳動 206 if (Math.abs((pos.top - this.mouseY) - this.curTop) < 10) { e.preventDefault(); return false; } 207 208 //先獲取相對容器的位置,在將兩個鼠標位置相減 209 this.curTop = pos.top - this.mouseY; 210 211 var resetData = this._getResetData(this.curTop); 212 if (resetData.needReset) { 213 this.curTop = this._resetEdge(this.curTop); 214 } 215 216 this.dragEl.css('top', this.curTop + 'px'); 217 this._setScrollTop(this.curTop); 218 e.preventDefault(); 219 220 }, 221 _touchEnd: function (e) { 222 if (this.cooling) { e.preventDefault(); return false; } 223 if (Math.abs(this.oTop - this.curTop) < 10) { e.preventDefault(); return false; } 224 //一次動做結束,開啓冷卻時間 225 this.cooling = true; 226 var scope = this; 227 this.timeGap = e.timeStamp - this.touchTime; 228 var flag = this.oTop < this.curTop ? 1 : -1; //判斷是向上仍是向下滾動 229 this.moveState = flag > 0 ? 'up' : 'down'; 230 231 var step = parseInt(this.timeGap / 10 - 10); 232 step = step > 0 ? step : 0; 233 var speed = this.animateParam[step] || 0; 234 var increment = speed * this.steplen * flag; 235 var top = this.curTop; 236 top += increment; 237 238 var resetData = this._getResetData(top); 239 if (resetData.needReset) { 240 top = this._resetEdge(top); 241 speed = 0; 242 } 243 244 //!!!此處動畫可能致使數據不一樣步,後期改造須要加入冷卻時間 245 if (this.oTop != this.curTop && this.curTop != top) { 246 var duration = 100 + (speed * 20); 247 top += increment; 248 this.dragEl.animate({ 249 top: top + 'px' 250 }, duration, 'linear', function () { 251 scope.reset.call(scope, top); 252 253 }); 254 this._setScrollTop(top, duration); 255 } else { 256 this.isMoveing = false; 257 this.oTop = top; 258 this.reset(top); 259 this.cooling = false; //關閉冷卻時間 260 } 261 this._hideScroll(); 262 e.preventDefault(); 263 }, 264 _resetEdge: function (top) { 265 var h1 = parseInt(this.wrapperHeight / 3); 266 var h2 = parseInt(this.dragHeight * (-1) + this.wrapperHeight * (2 / 3)); 267 if (top > 0 && top > h1) top = h1; 268 if (top < 0 && top < h2) top = h2; 269 return top; 270 }, 271 _getResetData: function (top) { 272 var needReset = false; 273 var sTop = top; 274 if (top < (-1) * (this.dragHeight - this.wrapperHeight)) { top = (-1) * (this.dragHeight - this.wrapperHeight); needReset = true; } 275 if (top > 0) { top = 0; needReset = true; } 276 277 return { 278 top: top, 279 sTop: sTop, 280 needReset: needReset 281 }; 282 }, 283 //超出限制後位置還原 284 reset: function (top) { 285 var scope = this; 286 var needReset = this._getResetData(top).needReset; 287 var top = this._getResetData(top).top; 288 289 if (needReset) { 290 scope.dragEl.animate({ 291 top: top + 'px' 292 }, 50, 'linear', function () { 293 scope._reset(top); 294 scope._setScrollTop(top); 295 296 }); 297 } else { 298 scope._reset(top); 299 } 300 }, 301 _reset: function (top) { 302 this.oTop = top; 303 this.curTop = top; 304 this.isMoveing = false; 305 this.cooling = false; //關閉冷卻時間 306 }, 307 //獲取鼠標信息 308 getMousePos: function (event) { 309 var top, left; 310 top = Math.max(document.body.scrollTop, document.documentElement.scrollTop); 311 left = Math.max(document.body.scrollLeft, document.documentElement.scrollLeft); 312 return { 313 top: top + event.clientY, 314 left: left + event.clientX 315 }; 316 } 317 }; 318 new Scroll({ wrapper: $('#body'), body: $('#wrapper') }); 319 </script> 320 </body> 321 </html>
這個是咱們第一步造成的代碼,他具備如下問題待解決:
① 因爲是CSS3實現的動畫,不能保存狀態,因此咱們再次點擊時候不能中止動畫
② 未使用CSS3的transform,因此整個功能暫不支持3D加速
③ 因爲touch時候的e.preventDefault,因此其中的文本框等在手機上不能獲取焦點
以上焦點即是咱們接下來須要解決的地方,上面提出了三大問題,咱們這裏來一一解決
各位看到上面實現動畫的方法是經過變化元素的Top實現的,這樣作原來有一個好處就是能夠向下兼容,可是對於移動端來講意義不大
事實上這裏的top實現動畫變爲translate實現動畫更爲舒服,緣由是手機對CSS3動畫作了處理,能夠開啓硬件加速
咱們能夠在瀏覽器中用css開啓硬件加速,使GPU (Graphics Processing Unit) 發揮功能,從而提高性能
CSS animations, transforms 以及 transitions 不會自動開啓GPU加速,而是由瀏覽器的緩慢的軟件渲染引擎來執行。
.cube { -webkit-transform: translate3d(250px,250px,250px) rotate3d(250px,250px,250px,-120deg) scale3d(0.5, 0.5, 0.5); }
以上代碼便會開啓硬件加速,因此咱們這裏的對應關係是這樣的:
top=>translate3d(0, 0, 0)
加速是好的,濫用可能引發性能問題,並且ios下動畫可能產生抖動現象,這個各位必定要注意,因而經過這個,咱們修改咱們的代碼:
http://sandbox.runjs.cn/show/wqw1lpcl(請用webkit手機對比)
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 <meta name="viewport" content="width=device-width,initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no"> 6 <style type="text/css"> 7 body, div, dl, dt, dd, ul, ol, li, h1, h2, h3, h4, h5, h6, pre, code, form, fieldset, legend, input, textarea, p, blockquote, th, td, hr, button, article, aside, details, figcaption, figure, footer, header, hgroup, menu, nav, section { margin: 0; padding: 0; } 8 body { font: normal 14px/1.5 "Arial" , "Lucida Grande" ,Verdana, "Microsoft YaHei" , "hei"; -webkit-font-smoothing: antialiased; color: #000; background: #ccc; } 9 header { position: absolute; top: 0; left: 0; width: 100%; height: 48px; background-color: #1491c5; } 10 footer { position: absolute; bottom: 0; left: 0; width: 100%; height: 48px; background-color: #1491c5; } 11 h1 { display: block; font-size: 2em; font-weight: bold; font-weight: 500; text-align: center; color: White; } 12 13 #body { position: absolute; top: 50px; bottom: 50px; background: #fff; width: 100%; overflow: hidden; } 14 #wrapper { width: 100%; } 15 16 17 #wrapper li { padding: 0 10px; height: 40px; line-height: 40px; border-bottom: 1px solid #ccc; border-top: 1px solid #fff; background-color: #fafafa; font-size: 14px; } 18 </style> 19 <script id="others_zepto_10rc1" type="text/javascript" class="library" src="/js/sandbox/other/zepto.min.js"></script> 20 </head> 21 <body> 22 <header id="header"> 23 <h1> 24 Header</h1> 25 </header> 26 <div id="body"> 27 <div id="wrapper"> 28 body 29 <ul> 30 <li>Pretty row 1</li> 31 <li>Pretty row 2</li> 32 <li>Pretty row 3</li> 33 <li>Pretty row 4</li> 34 <li> 35 <input type="text"></li> 36 <li>Pretty row 6</li> 37 <li>Pretty row 7</li> 38 <li>Pretty row 8</li> 39 <li> 40 <input type="checkbox"></li> 41 <li>Pretty row 10</li> 42 <li>Pretty row 11</li> 43 <li>Pretty row 12</li> 44 <li> 45 <input type="radio"></li> 46 <li>Pretty row 14</li> 47 <li>Pretty row 15</li> 48 <li>Pretty row 16</li> 49 <li> 50 <textarea></textarea></li> 51 <li>Pretty row 18</li> 52 <li>Pretty row 19</li> 53 <li>Pretty row 20</li> 54 <li> 55 <select> 56 <option>option</option> 57 </select></li> 58 </ul> 59 <hr /> 60 <ul> 61 62 <li>Pretty row 41</li> 63 <li>Pretty row 42</li> 64 <li>Pretty row 43</li> 65 <li>Pretty row 44</li> 66 <li>Pretty row 45</li> 67 <li>Pretty row 46</li> 68 <li>Pretty row 47</li> 69 <li>Pretty row 48</li> 70 <li>Pretty row 49</li> 71 <li>Pretty row 50</li> 72 </ul> 73 </div> 74 </div> 75 <footer> 76 <h1> 77 Footer</h1> 78 </footer> 79 <script src="zepto.js" type="text/javascript"></script> 80 <script type="text/javascript"> 81 var Scroll = function (opts) { 82 opts = opts || {}; 83 //檢測設備事件支持,肯定使用鼠標事件或者touch事件 84 this._checkEventCompatibility(); 85 this._setBaseParam(opts); 86 this._addEvent(); 87 88 this._initScrollBar(); 89 }; 90 91 Scroll.prototype = { 92 constructor: Scroll, 93 //檢測設備事件兼容 94 _checkEventCompatibility: function () { 95 var isTouch = 'ontouchstart' in document.documentElement; 96 isTouch = true; 97 98 this.start = isTouch ? 'touchstart' : 'mousedown'; 99 this.move = isTouch ? 'touchmove' : 'mousemove'; 100 this.end = isTouch ? 'touchend' : 'mouseup'; 101 this.startFn; 102 this.moveFn; 103 this.endFn; 104 }, 105 //基本參數設置 106 _setBaseParam: function (opts) { 107 this.timeGap = 0; //時間間隔 108 this.touchTime = 0; //開始時間 109 this.isMoveing = false; //是否正在移動 110 this.moveState = 'up'; //移動狀態,up right down left 111 this.oTop = 0; //拖動前的top值 112 this.curTop = 0; //當前容器top 113 this.mouseY = 0; //鼠標第一次點下時相對父容器的位置 114 this.animateParam = opts.animateParam || [10, 8, 6, 5, 4, 3, 2, 1, 0, 0, 0]; //動畫參數 115 this.cooling = true; //是否處於冷卻時間 116 this.steplen = 25; //動畫步長 117 118 this.wrapper = opts.wrapper || $('body'); 119 this.dragEl = opts.body; 120 this.wrapper.css({ 'position': 'absolute', 'overflow': 'hidden' }); 121 this.dragEl.css('position', 'absolute'); 122 this.wrapper.append(this.dragEl); 123 }, 124 _initScrollBar: function () { 125 if (!this.dragHeight) { 126 this.dragHeight = this.dragEl.offset().height; //拖動元素高度 127 this.wrapperHeight = this.wrapper.offset().height; 128 } 129 //滾動條縮放比例 130 this.scrollProportion = this.wrapperHeight / this.dragHeight; 131 this.isNeedScrollBar = true; 132 //該種狀況無需滾動條 133 if (this.scrollProportion >= 1) { 134 this.isNeedScrollBar = false; ; 135 return false; 136 } 137 //滾動條 138 this.scrollBar = $('<div style="background-color: rgba(0, 0, 0, 0.498039);border: 1px solid rgba(255, 255, 255, 0.901961); width: 5px; border-radius: 3px; position: absolute; right: 1px; opacity: 0.2; "></div>'); 139 this.wrapper.append(this.scrollBar); 140 this.scrollHeight = parseInt(this.scrollProportion * this.wrapperHeight); 141 this.scrollBar.css('height', this.scrollHeight); 142 }, 143 _setScrollTop: function (top, duration) { 144 //滾動條高度 145 if (this.isNeedScrollBar) { 146 top = this._getResetData(top).sTop; 147 top = top < 0 ? (top + 10) : top; 148 149 var scrollTop = top * (-1); 150 if (typeof duration == 'number') { 151 var _top = parseInt(scrollTop * this.scrollProportion) + 'px'; 152 this.scrollBar.animate({ 153 '-webkit-transform': 'translate3d(0, ' + _top + ', 0)' 154 }, duration, 'linear'); 155 156 } else { 157 var _st = parseInt(scrollTop * this.scrollProportion) 158 this.scrollBar.css('-webkit-transform', 'translate3d(0, ' + _st + 'px, 0)'); 159 } 160 this.scrollBar.css('opacity', '0.8'); 161 } 162 }, 163 _hideScroll: function () { 164 if (this.isNeedScrollBar) { 165 this.scrollBar.css({ 'opacity': '0.2' }); 166 } 167 }, 168 _addEvent: function () { 169 var scope = this; 170 this.startFn = function (e) { 171 scope._touchStart.call(scope, e); 172 }; 173 this.moveFn = function (e) { 174 scope._touchMove.call(scope, e); 175 }; 176 this.endFn = function (e) { 177 scope._touchEnd.call(scope, e); 178 }; 179 this.dragEl[0].addEventListener(this.start, this.startFn, false); 180 document.addEventListener(this.move, this.moveFn, false); 181 document.addEventListener(this.end, this.endFn, false); 182 }, 183 removeEvent: function () { 184 this.dragEl[0].removeEventListener(this.start, this.startFn); 185 document.removeEventListener(this.move, this.moveFn); 186 document.removeEventListener(this.end, this.endFn); 187 }, 188 _touchStart: function (e) { 189 var scope = this; 190 if (this.isMoveing) { e.preventDefault(); return false; } 191 //非運動狀況關閉冷卻時間 192 this.cooling = false; 193 this.touchTime = e.timeStamp; 194 pos = this.getMousePos((e.changedTouches && e.changedTouches[0]) || e); 195 // var top = parseFloat(this.dragEl.css('top')) || 0; 196 var top = this._cssTranslate(this.dragEl); 197 this.mouseY = pos.top - top; 198 }, 199 _touchMove: function (e) { 200 if (this.cooling) { e.preventDefault(); return false; } 201 202 this.isMoveing = true; 203 204 var pos = this.getMousePos((e.changedTouches && e.changedTouches[0]) || e); 205 206 //防止點擊時候跳動 207 if (Math.abs((pos.top - this.mouseY) - this.curTop) < 10) { e.preventDefault(); return false; } 208 209 //先獲取相對容器的位置,在將兩個鼠標位置相減 210 this.curTop = pos.top - this.mouseY; 211 212 var resetData = this._getResetData(this.curTop); 213 if (resetData.needReset) { 214 this.curTop = this._resetEdge(this.curTop); 215 } 216 217 // this.dragEl.css('top', this.curTop + 'px'); 218 this._cssTranslate(this.dragEl, this.curTop); 219 220 this._setScrollTop(this.curTop); 221 e.preventDefault(); 222 223 }, 224 _touchEnd: function (e) { 225 if (this.cooling) { e.preventDefault(); return false; } 226 if (Math.abs(this.oTop - this.curTop) < 10) { e.preventDefault(); return false; } 227 //一次動做結束,開啓冷卻時間 228 this.cooling = true; 229 var scope = this; 230 this.timeGap = e.timeStamp - this.touchTime; 231 var flag = this.oTop < this.curTop ? 1 : -1; //判斷是向上仍是向下滾動 232 this.moveState = flag > 0 ? 'up' : 'down'; 233 234 var step = parseInt(this.timeGap / 10 - 10); 235 step = step > 0 ? step : 0; 236 var speed = this.animateParam[step] || 0; 237 var increment = speed * this.steplen * flag; 238 var top = this.curTop; 239 top += increment; 240 241 var resetData = this._getResetData(top); 242 if (resetData.needReset) { 243 top = this._resetEdge(top); 244 speed = 0; 245 } 246 247 //!!!此處動畫可能致使數據不一樣步,後期改造須要加入冷卻時間 248 if (this.oTop != this.curTop && this.curTop != top) { 249 var duration = 100 + (speed * 20); 250 top += increment; 251 this.dragEl.animate({ 252 '-webkit-transform': 'translate3d(0, ' + top + 'px, 0)' 253 }, duration, 'linear', function () { 254 scope.reset.call(scope, top); 255 256 }); 257 this._setScrollTop(top, duration); 258 } else { 259 this.isMoveing = false; 260 this.oTop = top; 261 this.reset(top); 262 this.cooling = false; //關閉冷卻時間 263 } 264 this._hideScroll(); 265 e.preventDefault(); 266 }, 267 _resetEdge: function (top) { 268 var h1 = parseInt(this.wrapperHeight / 3); 269 var h2 = parseInt(this.dragHeight * (-1) + this.wrapperHeight * (2 / 3)); 270 if (top > 0 && top > h1) top = h1; 271 if (top < 0 && top < h2) top = h2; 272 return top; 273 }, 274 _getResetData: function (top) { 275 var needReset = false; 276 var sTop = top; 277 if (top < (-1) * (this.dragHeight - this.wrapperHeight)) { top = (-1) * (this.dragHeight - this.wrapperHeight); needReset = true; } 278 if (top > 0) { top = 0; needReset = true; } 279 280 return { 281 top: top, 282 sTop: sTop, 283 needReset: needReset 284 }; 285 }, 286 //超出限制後位置還原 287 reset: function (top) { 288 var scope = this; 289 var needReset = this._getResetData(top).needReset; 290 var top = this._getResetData(top).top; 291 292 if (needReset) { 293 scope.dragEl.animate({ 294 '-webkit-transform': 'translate3d(0, ' + top + 'px, 0)' 295 296 }, 50, 'linear', function () { 297 scope._reset(top); 298 scope._setScrollTop(top); 299 300 }); 301 } else { 302 scope._reset(top); 303 } 304 }, 305 _reset: function (top) { 306 this.oTop = top; 307 this.curTop = top; 308 this.isMoveing = false; 309 this.cooling = false; //關閉冷卻時間 310 }, 311 //暫時僅用於,操做Y值 312 _cssTranslate: function (el, y) { 313 if (!el) return 0; 314 if (typeof y == 'number') { 315 el.css('-webkit-transform', 'translate3d(0, ' + y + 'px, 0)'); 316 } 317 var data = /\((.*)\)/.exec(el.css('-webkit-transform')); 318 if (data && typeof data[1] == 'string') data = data[1].split(','); 319 if (data && typeof data[1] == 'string') return parseInt(data[1]); 320 return 0; 321 }, 322 //獲取鼠標信息 323 getMousePos: function (event) { 324 var top, left; 325 top = Math.max(document.body.scrollTop, document.documentElement.scrollTop); 326 left = Math.max(document.body.scrollLeft, document.documentElement.scrollLeft); 327 return { 328 top: top + event.clientY, 329 left: left + event.clientX 330 }; 331 } 332 }; 333 new Scroll({ wrapper: $('#body'), body: $('#wrapper') }); 334 </script> 335 </body> 336 </html>
PS:對比下來,我想說,硬件加速的感受真他媽爽!!!!這段代碼沒有過多測試,有問題請留言
要中止CSS動畫,而且要保存CSS的狀態,這個問題其實在三個問題中,我認爲是最難的,由於咱們可能遇到以下需求:
① 移動過程手指觸摸屏幕,動畫中止
② 連續滑動時候須要動畫加速
我這裏天然處理不到這麼複雜的問題,因此就先實現中止動畫便可
<div id="wrapper" style="position: absolute; -webkit-transform: translate3d(0px, -558px, 0px); -webkit-transition: -webkit-transform 20.1s linear; transition: -webkit-transform 20.1s linear;"> </div>
這個就是zepto一次動畫得到的參數,我故意將時間設置的很長,咱們要在點擊時候立刻獲取transform,而且從新設置
http://sandbox.runjs.cn/show/vgekfj8f
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 <meta name="viewport" content="width=device-width,initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no"> 6 <style type="text/css"> 7 body, div, dl, dt, dd, ul, ol, li, h1, h2, h3, h4, h5, h6, pre, code, form, fieldset, legend, input, textarea, p, blockquote, th, td, hr, button, article, aside, details, figcaption, figure, footer, header, hgroup, menu, nav, section { margin: 0; padding: 0; } 8 body { font: normal 14px/1.5 "Arial" , "Lucida Grande" ,Verdana, "Microsoft YaHei" , "hei"; -webkit-font-smoothing: antialiased; color: #000; background: #ccc; } 9 header { position: absolute; top: 0; left: 0; width: 100%; height: 48px; background-color: #1491c5; } 10 footer { position: absolute; bottom: 0; left: 0; width: 100%; height: 48px; background-color: #1491c5; } 11 h1 { display: block; font-size: 2em; font-weight: bold; font-weight: 500; text-align: center; color: White; } 12 13 #body { position: absolute; top: 50px; bottom: 50px; background: #fff; width: 100%; overflow: hidden; } 14 #wrapper { width: 100%; } 15 16 17 #wrapper li { padding: 0 10px; height: 40px; line-height: 40px; border-bottom: 1px solid #ccc; border-top: 1px solid #fff; background-color: #fafafa; font-size: 14px; } 18 </style> 19 <script id="others_zepto_10rc1" type="text/javascript" class="library" src="/js/sandbox/other/zepto.min.js"></script> 20 </head> 21 <body> 22 <header id="header"> 23 <h1> 24 Header</h1> 25 </header> 26 <div id="body"> 27 <div id="wrapper"> 28 body 29 <ul> 30 <li>Pretty row 1</li> 31 <li>Pretty row 2</li> 32 <li>Pretty row 3</li> 33 <li>Pretty row 4</li> 34 <li> 35 <input type="text"></li> 36 <li>Pretty row 6</li> 37 <li>Pretty row 7</li> 38 <li>Pretty row 8</li> 39 <li> 40 <input type="checkbox"></li> 41 <li>Pretty row 10</li> 42 <li>Pretty row 11</li> 43 <li>Pretty row 12</li> 44 <li> 45 <input type="radio"></li> 46 <li>Pretty row 14</li> 47 <li>Pretty row 15</li> 48 <li>Pretty row 16</li> 49 <li> 50 <textarea></textarea></li> 51 <li>Pretty row 18</li> 52 <li>Pretty row 19</li> 53 <li>Pretty row 20</li> 54 <li> 55 <select> 56 <option>option</option> 57 </select></li> 58 </ul> 59 <hr /> 60 <ul> 61 62 <li>Pretty row 41</li> 63 <li>Pretty row 42</li> 64 <li>Pretty row 43</li> 65 <li>Pretty row 44</li> 66 <li>Pretty row 45</li> 67 <li>Pretty row 46</li> 68 <li>Pretty row 47</li> 69 <li>Pretty row 48</li> 70 <li>Pretty row 49</li> 71 <li>Pretty row 50</li> 72 </ul> 73 </div> 74 </div> 75 <footer> 76 <h1> 77 Footer</h1> 78 </footer> 79 <script src="zepto.js" type="text/javascript"></script> 80 <script type="text/javascript"> 81 var Scroll = function (opts) { 82 opts = opts || {}; 83 //檢測設備事件支持,肯定使用鼠標事件或者touch事件 84 this._checkEventCompatibility(); 85 this._setBaseParam(opts); 86 this._addEvent(); 87 88 this._initScrollBar(); 89 }; 90 91 Scroll.prototype = { 92 constructor: Scroll, 93 //檢測設備事件兼容 94 _checkEventCompatibility: function () { 95 var isTouch = 'ontouchstart' in document.documentElement; 96 // isTouch = true; 97 98 this.start = isTouch ? 'touchstart' : 'mousedown'; 99 this.move = isTouch ? 'touchmove' : 'mousemove'; 100 this.end = isTouch ? 'touchend' : 'mouseup'; 101 this.startFn; 102 this.moveFn; 103 this.endFn; 104 }, 105 //基本參數設置 106 _setBaseParam: function (opts) { 107 this.timeGap = 0; //時間間隔 108 this.touchTime = 0; //開始時間 109 this.isMoveing = false; //是否正在移動 110 this.moveState = 'up'; //移動狀態,up right down left 111 this.oTop = 0; //拖動前的top值 112 this.curTop = 0; //當前容器top 113 this.mouseY = 0; //鼠標第一次點下時相對父容器的位置 114 this.animateParam = opts.animateParam || [10, 8, 6, 5, 4, 3, 2, 1, 0, 0, 0]; //動畫參數 115 this.cooling = true; //是否處於冷卻時間 116 this.steplen = 25; //動畫步長 117 118 this.wrapper = opts.wrapper || $('body'); 119 this.dragEl = opts.body; 120 this.wrapper.css({ 'position': 'absolute', 'overflow': 'hidden' }); 121 this.dragEl.css('position', 'absolute'); 122 this.wrapper.append(this.dragEl); 123 }, 124 _initScrollBar: function () { 125 if (!this.dragHeight) { 126 this.dragHeight = this.dragEl.offset().height; //拖動元素高度 127 this.wrapperHeight = this.wrapper.offset().height; 128 } 129 //滾動條縮放比例 130 this.scrollProportion = this.wrapperHeight / this.dragHeight; 131 this.isNeedScrollBar = true; 132 //該種狀況無需滾動條 133 if (this.scrollProportion >= 1) { 134 this.isNeedScrollBar = false; ; 135 return false; 136 } 137 //滾動條 138 this.scrollBar = $('<div style="background-color: rgba(0, 0, 0, 0.498039);border: 1px solid rgba(255, 255, 255, 0.901961); width: 5px; border-radius: 3px; position: absolute; right: 1px; opacity: 0.2; "></div>'); 139 this.wrapper.append(this.scrollBar); 140 this.scrollHeight = parseInt(this.scrollProportion * this.wrapperHeight); 141 this.scrollBar.css('height', this.scrollHeight); 142 }, 143 _setScrollTop: function (top, duration) { 144 //滾動條高度 145 if (this.isNeedScrollBar) { 146 top = this._getResetData(top).sTop; 147 top = top < 0 ? (top + 10) : top; 148 149 var scrollTop = top * (-1); 150 if (typeof duration == 'number') { 151 var _top = parseInt(scrollTop * this.scrollProportion) + 'px'; 152 this.scrollBar.animate({ 153 '-webkit-transform': 'translate3d(0, ' + _top + ', 0)' 154 }, duration, 'linear'); 155 156 } else { 157 var _st = parseInt(scrollTop * this.scrollProportion) 158 this.scrollBar.css('-webkit-transform', 'translate3d(0, ' + _st + 'px, 0)'); 159 } 160 this.scrollBar.css('opacity', '0.8'); 161 } 162 }, 163 _hideScroll: function () { 164 if (this.isNeedScrollBar) { 165 this.scrollBar.css({ 'opacity': '0.2' }); 166 } 167 }, 168 _addEvent: function () { 169 var scope = this; 170 this.startFn = function (e) { 171 scope._touchStart.call(scope, e); 172 }; 173 this.moveFn = function (e) { 174 scope._touchMove.call(scope, e); 175 }; 176 this.endFn = function (e) { 177 scope._touchEnd.call(scope, e); 178 }; 179 this.dragEl[0].addEventListener(this.start, this.startFn, false); 180 document.addEventListener(this.move, this.moveFn, false); 181 document.addEventListener(this.end, this.endFn, false); 182 }, 183 removeEvent: function () { 184 this.dragEl[0].removeEventListener(this.start, this.startFn); 185 document.removeEventListener(this.move, this.moveFn); 186 document.removeEventListener(this.end, this.endFn); 187 }, 188 _touchStart: function (e) { 189 var scope = this; 190 window.dragEl = this.dragEl; 191 192 if (this.isMoveing) { 193 194 195 196 var el = this.dragEl[0]; 197 var computedStyle = document.defaultView.getComputedStyle(el, null); 198 // computedStyle.getPropertyValue("width"); 199 200 var top = 0; 201 var data = /\((.*)\)/.exec(computedStyle.getPropertyValue("-webkit-transform")); 202 if (typeof data == 'object') data = data[1].split(','); 203 if (typeof data == 'object') top = parseInt(data[5]); 204 console.log(top); 205 206 207 this.dragEl.css('-webkit-transition', '-webkit-transform 0s linear'); 208 this.dragEl.css('transition', '-webkit-transform 0s linear'); 209 210 this._cssTranslate(this.dragEl, top); 211 this._setScrollTop(top); 212 213 214 this.isMoveing = false; 215 e.preventDefault(); return false; 216 } 217 218 this.clickEl = e.target; 219 220 221 222 223 //非運動狀況關閉冷卻時間 224 this.cooling = false; 225 this.touchTime = e.timeStamp; 226 pos = this.getMousePos((e.changedTouches && e.changedTouches[0]) || e); 227 // var top = parseFloat(this.dragEl.css('top')) || 0; 228 var top = this._cssTranslate(this.dragEl); 229 this.mouseY = pos.top - top; 230 }, 231 _touchMove: function (e) { 232 if (this.cooling) { e.preventDefault(); return false; } 233 234 this.isMoveing = true; 235 236 var pos = this.getMousePos((e.changedTouches && e.changedTouches[0]) || e); 237 238 //防止點擊時候跳動 239 if (Math.abs((pos.top - this.mouseY) - this.curTop) < 10) { e.preventDefault(); return false; } 240 241 //先獲取相對容器的位置,在將兩個鼠標位置相減 242 this.curTop = pos.top - this.mouseY; 243 244 var resetData = this._getResetData(this.curTop); 245 if (resetData.needReset) { 246 this.curTop = this._resetEdge(this.curTop); 247 } 248 249 // this.dragEl.css('top', this.curTop + 'px'); 250 this._cssTranslate(this.dragEl, this.curTop); 251 252 this._setScrollTop(this.curTop); 253 e.preventDefault(); 254 255 }, 256 _touchEnd: function (e) { 257 258 if (this._needFocus(this.clickEl)) { 259 $(this.clickEl).focus(); 260 return; 261 } 262 263 264 if (this.cooling) { e.preventDefault(); return false; } 265 if (Math.abs(this.oTop - this.curTop) < 10) { e.preventDefault(); return false; } 266 //一次動做結束,開啓冷卻時間 267 this.cooling = true; 268 var scope = this; 269 this.timeGap = e.timeStamp - this.touchTime; 270 var flag = this.oTop < this.curTop ? 1 : -1; //判斷是向上仍是向下滾動 271 this.moveState = flag > 0 ? 'up' : 'down'; 272 273 var step = parseInt(this.timeGap / 10 - 10); 274 step = step > 0 ? step : 0; 275 var speed = this.animateParam[step] || 0; 276 var increment = speed * this.steplen * flag; 277 var top = this.curTop; 278 top += increment; 279 280 var resetData = this._getResetData(top); 281 if (resetData.needReset) { 282 top = this._resetEdge(top); 283 speed = 0; 284 } 285 286 speed = 1000; 287 288 //!!!此處動畫可能致使數據不一樣步,後期改造須要加入冷卻時間 289 if (this.oTop != this.curTop && this.curTop != top) { 290 var duration = 100 + (speed * 20); 291 top += increment; 292 this.dragEl.animate({ 293 '-webkit-transform': 'translate3d(0, ' + top + 'px, 0)' 294 }, duration, 'linear', function () { 295 scope.reset.call(scope, top); 296 297 }); 298 this._setScrollTop(top, duration); 299 } else { 300 this.isMoveing = false; 301 this.oTop = top; 302 this.reset(top); 303 this.cooling = false; //關閉冷卻時間 304 } 305 this._hideScroll(); 306 e.preventDefault(); 307 }, 308 _resetEdge: function (top) { 309 var h1 = parseInt(this.wrapperHeight / 3); 310 var h2 = parseInt(this.dragHeight * (-1) + this.wrapperHeight * (2 / 3)); 311 if (top > 0 && top > h1) top = h1; 312 if (top < 0 && top < h2) top = h2; 313 return top; 314 }, 315 _getResetData: function (top) { 316 var needReset = false; 317 var sTop = top; 318 if (top < (-1) * (this.dragHeight - this.wrapperHeight)) { top = (-1) * (this.dragHeight - this.wrapperHeight); needReset = true; } 319 if (top > 0) { top = 0; needReset = true; } 320 321 return { 322 top: top, 323 sTop: sTop, 324 needReset: needReset 325 }; 326 }, 327 //超出限制後位置還原 328 reset: function (top) { 329 var scope = this; 330 var needReset = this._getResetData(top).needReset; 331 var top = this._getResetData(top).top; 332 333 if (needReset) { 334 scope.dragEl.animate({ 335 '-webkit-transform': 'translate3d(0, ' + top + 'px, 0)' 336 337 }, 50, 'linear', function () { 338 scope._reset(top); 339 scope._setScrollTop(top); 340 341 }); 342 } else { 343 scope._reset(top); 344 } 345 }, 346 _reset: function (top) { 347 this.oTop = top; 348 this.curTop = top; 349 this.isMoveing = false; 350 this.cooling = false; //關閉冷卻時間 351 }, 352 //暫時僅用於,操做Y值 353 _cssTranslate: function (el, y) { 354 if (!el) return 0; 355 if (typeof y == 'number') { 356 el.css('-webkit-transform', 'translate3d(0, ' + y + 'px, 0)'); 357 } 358 var data = /\((.*)\)/.exec(el.css('-webkit-transform')); 359 if (data && typeof data[1] == 'string') data = data[1].split(','); 360 if (data && typeof data[1] == 'string') return parseInt(data[1]); 361 return 0; 362 }, 363 _needFocus: function (el) { 364 switch (el.nodeName.toLowerCase()) { 365 case 'textarea': 366 case 'select': 367 return true; 368 case 'input': 369 switch (el.type) { 370 case 'button': 371 case 'checkbox': 372 case 'file': 373 case 'image': 374 case 'radio': 375 case 'submit': 376 return false; 377 } 378 return !el.disabled && !el.readOnly; 379 default: 380 return (/\bneedfocus\b/).test(el.className); 381 } 382 }, 383 //獲取鼠標信息 384 getMousePos: function (event) { 385 var top, left; 386 top = Math.max(document.body.scrollTop, document.documentElement.scrollTop); 387 left = Math.max(document.body.scrollLeft, document.documentElement.scrollLeft); 388 return { 389 top: top + event.clientY, 390 left: left + event.clientX 391 }; 392 } 393 }; 394 new Scroll({ wrapper: $('#body'), body: $('#wrapper') }); 395 </script> 396 </body> 397 </html>
PS:時間比較晚了,代碼未作檢測,各位包含
因爲e.preventDefault的效果,因此咱們裏面的按鈕點擊一鍵文本框獲取焦點有點很差使,我這裏主要解決文本獲取焦點便可
咱們在touchstart時候能夠獲取e.target,在touchend時候如果判斷是一次點擊事件而且target爲文本框的話,便獲取焦點。
PS:我這裏由於暫時不用做生產,先簡單實現便可
http://sandbox.runjs.cn/show/djkrwwno
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 <meta name="viewport" content="width=device-width,initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no"> 6 <style type="text/css"> 7 body, div, dl, dt, dd, ul, ol, li, h1, h2, h3, h4, h5, h6, pre, code, form, fieldset, legend, input, textarea, p, blockquote, th, td, hr, button, article, aside, details, figcaption, figure, footer, header, hgroup, menu, nav, section { margin: 0; padding: 0; } 8 body { font: normal 14px/1.5 "Arial" , "Lucida Grande" ,Verdana, "Microsoft YaHei" , "hei"; -webkit-font-smoothing: antialiased; color: #000; background: #ccc; } 9 header { position: absolute; top: 0; left: 0; width: 100%; height: 48px; background-color: #1491c5; } 10 footer { position: absolute; bottom: 0; left: 0; width: 100%; height: 48px; background-color: #1491c5; } 11 h1 { display: block; font-size: 2em; font-weight: bold; font-weight: 500; text-align: center; color: White; } 12 13 #body { position: absolute; top: 50px; bottom: 50px; background: #fff; width: 100%; overflow: hidden; } 14 #wrapper { width: 100%; } 15 16 17 #wrapper li { padding: 0 10px; height: 40px; line-height: 40px; border-bottom: 1px solid #ccc; border-top: 1px solid #fff; background-color: #fafafa; font-size: 14px; } 18 </style> 19 <script id="others_zepto_10rc1" type="text/javascript" class="library" src="/js/sandbox/other/zepto.min.js"></script> 20 </head> 21 <body> 22 <header id="header"> 23 <h1> 24 Header</h1> 25 </header> 26 <div id="body"> 27 <div id="wrapper"> 28 body 29 <ul> 30 <li>Pretty row 1</li> 31 <li>Pretty row 2</li> 32 <li>Pretty row 3</li> 33 <li>Pretty row 4</li> 34 <li> 35 <input type="text"></li> 36 <li>Pretty row 6</li> 37 <li>Pretty row 7</li> 38 <li>Pretty row 8</li> 39 <li> 40 <input type="checkbox"></li> 41 <li>Pretty row 10</li> 42 <li>Pretty row 11</li> 43 <li>Pretty row 12</li> 44 <li> 45 <input type="radio"></li> 46 <li>Pretty row 14</li> 47 <li>Pretty row 15</li> 48 <li>Pretty row 16</li> 49 <li> 50 <textarea></textarea></li> 51 <li>Pretty row 18</li> 52 <li>Pretty row 19</li> 53 <li>Pretty row 20</li> 54 <li> 55 <select> 56 <option>option</option> 57 </select></li> 58 </ul> 59 <hr /> 60 <ul> 61 62 <li>Pretty row 41</li> 63 <li>Pretty row 42</li> 64 <li>Pretty row 43</li> 65 <li>Pretty row 44</li> 66 <li>Pretty row 45</li> 67 <li>Pretty row 46</li> 68 <li>Pretty row 47</li> 69 <li>Pretty row 48</li> 70 <li>Pretty row 49</li> 71 <li>Pretty row 50</li> 72 </ul> 73 </div> 74 </div> 75 <footer> 76 <h1> 77 Footer</h1> 78 </footer> 79 <script src="zepto.js" type="text/javascript"></script> 80 <script type="text/javascript"> 81 var Scroll = function (opts) { 82 opts = opts || {}; 83 //檢測設備事件支持,肯定使用鼠標事件或者touch事件 84 this._checkEventCompatibility(); 85 this._setBaseParam(opts); 86 this._addEvent(); 87 88 this._initScrollBar(); 89 }; 90 91 Scroll.prototype = { 92 constructor: Scroll, 93 //檢測設備事件兼容 94 _checkEventCompatibility: function () { 95 var isTouch = 'ontouchstart' in document.documentElement; 96 97 this.start = isTouch ? 'touchstart' : 'mousedown'; 98 this.move = isTouch ? 'touchmove' : 'mousemove'; 99 this.end = isTouch ? 'touchend' : 'mouseup'; 100 this.startFn; 101 this.moveFn; 102 this.endFn; 103 }, 104 //基本參數設置 105 _setBaseParam: function (opts) { 106 this.timeGap = 0; //時間間隔 107 this.touchTime = 0; //開始時間 108 this.isMoveing = false; //是否正在移動 109 this.moveState = 'up'; //移動狀態,up right down left 110 this.oTop = 0; //拖動前的top值 111 this.curTop = 0; //當前容器top 112 this.mouseY = 0; //鼠標第一次點下時相對父容器的位置 113 this.animateParam = opts.animateParam || [10, 8, 6, 5, 4, 3, 2, 1, 0, 0, 0]; //動畫參數 114 this.cooling = true; //是否處於冷卻時間 115 this.steplen = 25; //動畫步長 116 117 this.wrapper = opts.wrapper || $('body'); 118 this.dragEl = opts.body; 119 this.wrapper.css({ 'position': 'absolute', 'overflow': 'hidden' }); 120 this.dragEl.css('position', 'absolute'); 121 this.wrapper.append(this.dragEl); 122 }, 123 _initScrollBar: function () { 124 if (!this.dragHeight) { 125 this.dragHeight = this.dragEl.offset().height; //拖動元素高度 126 this.wrapperHeight = this.wrapper.offset().height; 127 } 128 //滾動條縮放比例 129 this.scrollProportion = this.wrapperHeight / this.dragHeight; 130 this.isNeedScrollBar = true; 131 //該種狀況無需滾動條 132 if (this.scrollProportion >= 1) { 133 this.isNeedScrollBar = false; ; 134 return false; 135 } 136 //滾動條 137 this.scrollBar = $('<div style="background-color: rgba(0, 0, 0, 0.498039);border: 1px solid rgba(255, 255, 255, 0.901961); width: 5px; border-radius: 3px; position: absolute; right: 1px; opacity: 0.2; "></div>'); 138 this.wrapper.append(this.scrollBar); 139 this.scrollHeight = parseInt(this.scrollProportion * this.wrapperHeight); 140 this.scrollBar.css('height', this.scrollHeight); 141 }, 142 _setScrollTop: function (top, duration) { 143 //滾動條高度 144 if (this.isNeedScrollBar) { 145 top = this._getResetData(top).sTop; 146 top = top < 0 ? (top + 10) : top; 147 148 var scrollTop = top * (-1); 149 if (typeof duration == 'number') { 150 var _top = parseInt(scrollTop * this.scrollProportion) + 'px'; 151 this.scrollBar.animate({ 152 '-webkit-transform': 'translate3d(0, ' + _top + ', 0)' 153 }, duration, 'linear'); 154 155 } else { 156 var _st = parseInt(scrollTop * this.scrollProportion) 157 this.scrollBar.css('-webkit-transform', 'translate3d(0, ' + _st + 'px, 0)'); 158 } 159 this.scrollBar.css('opacity', '0.8'); 160 } 161 }, 162 _hideScroll: function () { 163 if (this.isNeedScrollBar) { 164 this.scrollBar.css({ 'opacity': '0.2' }); 165 } 166 }, 167 _addEvent: function () { 168 var scope = this; 169 this.startFn = function (e) { 170 scope._touchStart.call(scope, e); 171 }; 172 this.moveFn = function (e) { 173 scope._touchMove.call(scope, e); 174 }; 175 this.endFn = function (e) { 176 scope._touchEnd.call(scope, e); 177 }; 178 this.dragEl[0].addEventListener(this.start, this.startFn, false); 179 document.addEventListener(this.move, this.moveFn, false); 180 document.addEventListener(this.end, this.endFn, false); 181 }, 182 removeEvent: function () { 183 this.dragEl[0].removeEventListener(this.start, this.startFn); 184 document.removeEventListener(this.move, this.moveFn); 185 document.removeEventListener(this.end, this.endFn); 186 }, 187 _touchStart: function (e) { 188 var scope = this; 189 if (this.isMoveing) { e.preventDefault(); return false; } 190 191 this.clickEl = e.target; 192 193 //非運動狀況關閉冷卻時間 194 this.cooling = false; 195 this.touchTime = e.timeStamp; 196 pos = this.getMousePos((e.changedTouches && e.changedTouches[0]) || e); 197 // var top = parseFloat(this.dragEl.css('top')) || 0; 198 var top = this._cssTranslate(this.dragEl); 199 this.mouseY = pos.top - top; 200 }, 201 _touchMove: function (e) { 202 if (this.cooling) { e.preventDefault(); return false; } 203 204 this.isMoveing = true; 205 206 var pos = this.getMousePos((e.changedTouches && e.changedTouches[0]) || e); 207 208 //防止點擊時候跳動 209 if (Math.abs((pos.top - this.mouseY) - this.curTop) < 10) { e.preventDefault(); return false; } 210 211 //先獲取相對容器的位置,在將兩個鼠標位置相減 212 this.curTop = pos.top - this.mouseY; 213 214 var resetData = this._getResetData(this.curTop); 215 if (resetData.needReset) { 216 this.curTop = this._resetEdge(this.curTop); 217 } 218 219 // this.dragEl.css('top', this.curTop + 'px'); 220 this._cssTranslate(this.dragEl, this.curTop); 221 222 this._setScrollTop(this.curTop); 223 e.preventDefault(); 224 225 }, 226 _touchEnd: function (e) { 227 228 if (this._needFocus(this.clickEl)) { 229 $(this.clickEl).focus(); 230 return; 231 } 232 233 234 if (this.cooling) { e.preventDefault(); return false; } 235 if (Math.abs(this.oTop - this.curTop) < 10) { e.preventDefault(); return false; } 236 //一次動做結束,開啓冷卻時間 237 this.cooling = true; 238 var scope = this; 239 this.timeGap = e.timeStamp - this.touchTime; 240 var flag = this.oTop < this.curTop ? 1 : -1; //判斷是向上仍是向下滾動 241 this.moveState = flag > 0 ? 'up' : 'down'; 242 243 var step = parseInt(this.timeGap / 10 - 10); 244 step = step > 0 ? step : 0; 245 var speed = this.animateParam[step] || 0; 246 var increment = speed * this.steplen * flag; 247 var top = this.curTop; 248 top += increment; 249 250 var resetData = this._getResetData(top); 251 if (resetData.needReset) { 252 top = this._resetEdge(top); 253 speed = 0; 254 } 255 256 //!!!此處動畫可能致使數據不一樣步,後期改造須要加入冷卻時間 257 if (this.oTop != this.curTop && this.curTop != top) { 258 var duration = 100 + (speed * 20); 259 top += increment; 260 this.dragEl.animate({ 261 '-webkit-transform': 'translate3d(0, ' + top + 'px, 0)' 262 }, duration, 'linear', function () { 263 scope.reset.call(scope, top); 264 265 }); 266 this._setScrollTop(top, duration); 267 } else { 268 this.isMoveing = false; 269 this.oTop = top; 270 this.reset(top); 271 this.cooling = false; //關閉冷卻時間 272 } 273 this._hideScroll(); 274 e.preventDefault(); 275 }, 276 _resetEdge: function (top) { 277 var h1 = parseInt(this.wrapperHeight / 3); 278 var h2 = parseInt(this.dragHeight * (-1) + this.wrapperHeight * (2 / 3)); 279 if (top > 0 && top > h1) top = h1; 280 if (top < 0 && top < h2) top = h2; 281 return top; 282 }, 283 _getResetData: function (top) { 284 var needReset = false; 285 var sTop = top; 286 if (top < (-1) * (this.dragHeight - this.wrapperHeight)) { top = (-1) * (this.dragHeight - this.wrapperHeight); needReset = true; } 287 if (top > 0) { top = 0; needReset = true; } 288 289 return { 290 top: top, 291 sTop: sTop, 292 needReset: needReset 293 }; 294 }, 295 //超出限制後位置還原 296 reset: function (top) { 297 var scope = this; 298 var needReset = this._getResetData(top).needReset; 299 var top = this._getResetData(top).top; 300 301 if (needReset) { 302 scope.dragEl.animate({ 303 '-webkit-transform': 'translate3d(0, ' + top + 'px, 0)' 304 305 }, 50, 'linear', function () { 306 scope._reset(top); 307 scope._setScrollTop(top); 308 309 }); 310 } else { 311 scope._reset(top); 312 } 313 }, 314 _reset: function (top) { 315 this.oTop = top; 316 this.curTop = top; 317 this.isMoveing = false; 318 this.cooling = false; //關閉冷卻時間 319 }, 320 //暫時僅用於,操做Y值 321 _cssTranslate: function (el, y) { 322 if (!el) return 0; 323 if (typeof y == 'number') { 324 el.css('-webkit-transform', 'translate3d(0, ' + y + 'px, 0)'); 325 } 326 var data = /\((.*)\)/.exec(el.css('-webkit-transform')); 327 if (data && typeof data[1] == 'string') data = data[1].split(','); 328 if (data && typeof data[1] == 'string') return parseInt(data[1]); 329 return 0; 330 }, 331 _needFocus: function (el) { 332 switch (el.nodeName.toLowerCase()) { 333 case 'textarea': 334 case 'select': 335 return true; 336 case 'input': 337 switch (el.type) { 338 case 'button': 339 case 'checkbox': 340 case 'file': 341 case 'image': 342 case 'radio': 343 case 'submit': 344 return false; 345 } 346 return !el.disabled && !el.readOnly; 347 default: 348 return (/\bneedfocus\b/).test(el.className); 349 } 350 }, 351 //獲取鼠標信息 352 getMousePos: function (event) { 353 var top, left; 354 top = Math.max(document.body.scrollTop, document.documentElement.scrollTop); 355 left = Math.max(document.body.scrollLeft, document.documentElement.scrollLeft); 356 return { 357 top: top + event.clientY, 358 left: left + event.clientX 359 }; 360 } 361 }; 362 new Scroll({ wrapper: $('#body'), body: $('#wrapper') }); 363 </script> 364 </body> 365 </html>
如此,文本類標籤即可以得到焦點了,其它的東西,各位經過代碼本身搞下吧,如今只剩下中止動畫了......有點累
核心代碼:
1 if (this._needFocus(this.clickEl)) { 2 $(this.clickEl).focus(); 3 return; 4 } 5 _needFocus: function (el) { 6 switch (el.nodeName.toLowerCase()) { 7 case 'textarea': 8 case 'select': 9 return true; 10 case 'input': 11 switch (el.type) { 12 case 'button': 13 case 'checkbox': 14 case 'file': 15 case 'image': 16 case 'radio': 17 case 'submit': 18 return false; 19 } 20 return !el.disabled && !el.readOnly; 21 default: 22 return (/\bneedfocus\b/).test(el.className); 23 } 24 },
今天,咱們一塊兒實現了簡單的iScroll的功能,明天咱們一塊兒來進行源碼學習,看看iScroll到底有何優勢
若是您發現文中有何問題,請與我聯繫。