alloyTouch這個庫其實可以做很多事的, 比較抽象, 需要我們用戶好好的思考作者提供的實例屬性和一些回調方法(touchStart,
change, touchMove, pressMove, tap, touchEnd, touchCancel, reboundEnd, animationEnd, correctionEnd).
哇, 提供了這么多回調啊, 因為我也剛玩,用到的不多。
change回調很常用(比如上拉,下拉刷新要用到),配合touchStart, animationEnd等,看需求吧
touchEnd, animationEnd(寫輪播用到)。
因為我也剛用, 暫時沒怎么研究其他回調的用法,但是我都在源碼中標記了什麽時候觸發回調的。
原理說明:
alloyTouch庫其實是一個數字運動的庫,我再看源碼時,看見源碼里_end()中計算各種比值和目標值,目的就是讓dom在什么時間內運動到什么目標值。
當然對外也提供了to方法,讓dom運動到用戶指定的目標值。具體看源碼注釋。
源碼筆記:
由於demo沒全看完,所以有的注釋理解的不太到位。有問題,歡迎指正。
1 /* AlloyTouch v0.2.1 2 * By AlloyTeam http://www.alloyteam.com/ 3 * Github: https://github.com/AlloyTeam/AlloyTouch 4 * MIT Licensed. 5 * Sorrow.X --- 添加注釋,注釋純屬個人理解 6 */ 7 8 // 兼容不支持requestAnimationFrame的瀏覽器 9 ;(function () { 10 'use strict'; 11 12 if (!Date.now) 13 Date.now = function () { return new Date().getTime(); }; 14 15 var vendors = ['webkit', 'moz']; 16 for (var i = 0; i < vendors.length && !window.requestAnimationFrame; ++i) { 17 var vp = vendors[i]; 18 window.requestAnimationFrame = window[vp + 'RequestAnimationFrame']; 19 window.cancelAnimationFrame = (window[vp + 'CancelAnimationFrame'] 20 || window[vp + 'CancelRequestAnimationFrame']); 21 } 22 if (/iP(ad|hone|od).*OS 6/.test(window.navigator.userAgent) // iOS6 is buggy 23 || !window.requestAnimationFrame || !window.cancelAnimationFrame) { 24 var lastTime = 0; 25 window.requestAnimationFrame = function (callback) { 26 var now = Date.now(); 27 var nextTime = Math.max(lastTime + 16, now); 28 return setTimeout(function () { callback(lastTime = nextTime); }, 29 nextTime - now); 30 }; 31 window.cancelAnimationFrame = clearTimeout; 32 } 33 }()); 34 35 (function () { 36 37 // 給元素綁定事件, 默認冒泡 38 function bind(element, type, callback) { 39 element.addEventListener(type, callback, false); 40 }; 41 42 // 三次貝塞爾 43 function ease(x) { 44 return Math.sqrt(1 - Math.pow(x - 1, 2)); 45 }; 46 47 // 相反的三次貝塞爾 48 function reverseEase(y) { 49 return 1 - Math.sqrt(1 - y * y); 50 }; 51 52 // INPUT|TEXTAREA|BUTTON|SELECT這幾個標簽就不用阻止默認事件了 53 function preventDefaultTest(el, exceptions) { 54 for (var i in exceptions) { 55 if (exceptions[i].test(el[i])) { 56 return true; 57 }; 58 }; 59 return false; 60 }; 61 62 var AlloyTouch = function (option) { 63 64 this.element = typeof option.touch === "string" ? document.querySelector(option.touch) : option.touch; // 反饋觸摸的dom 65 this.target = this._getValue(option.target, this.element); // 運動的對象 66 this.vertical = this._getValue(option.vertical, true); // 不必需,默認是true代表監聽豎直方向touch 67 this.property = option.property; // 被滾動的屬性 68 this.tickID = 0; 69 70 this.initialValue = this._getValue(option.initialValue, this.target[this.property]); // 被運動的屬性的初始值,默認從Transform原始屬性拿值 71 this.target[this.property] = this.initialValue; // 給運動的屬性賦值 72 this.fixed = this._getValue(option.fixed, false); 73 this.sensitivity = this._getValue(option.sensitivity, 1); // 默認是1, 靈敏度 74 this.moveFactor = this._getValue(option.moveFactor, 1); // move時的運動系數 75 this.factor = this._getValue(option.factor, 1); // 系數 76 this.outFactor = this._getValue(option.outFactor, 0.3); // 外部系數 77 this.min = option.min; // 不必需,滾動屬性的最小值,越界會回彈 78 this.max = option.max; // 不必需,運動屬性的最大值,越界會回彈, 一般為0 79 this.deceleration = 0.0006; // 減速系數 80 this.maxRegion = this._getValue(option.maxRegion, 600); // 最大區域, 默認60 81 this.springMaxRegion = this._getValue(option.springMaxRegion, 60); // 彈動的最大值區域, 默認60 82 this.maxSpeed = option.maxSpeed; // 最大速度 83 this.hasMaxSpeed = !(this.maxSpeed === undefined); // 是否有最大速度屬性 84 this.lockDirection = this._getValue(option.lockDirection, true); // 鎖定方向 85 86 var noop = function () { }; // 空函數 87 this.touchStart = option.touchStart || noop; 88 this.change = option.change || noop; 89 this.touchMove = option.touchMove || noop; 90 this.pressMove = option.pressMove || noop; 91 this.tap = option.tap || noop; 92 this.touchEnd = option.touchEnd || noop; 93 this.touchCancel = option.touchCancel || noop; 94 this.reboundEnd = option.reboundEnd || noop; // 回彈回調 95 this.animationEnd = option.animationEnd || noop; 96 this.correctionEnd = option.correctionEnd || noop; // 修改回調 97 98 this.preventDefault = this._getValue(option.preventDefault, true); // 默認是true,是否阻止默認事件 99 this.preventDefaultException = { tagName: /^(INPUT|TEXTAREA|BUTTON|SELECT)$/ }; // 這幾個tag標簽,阻止默認事件例外 100 this.hasMin = !(this.min === undefined); // 是否有min,和max屬性 101 this.hasMax = !(this.max === undefined); 102 if (this.hasMin && this.hasMax && this.min > this.max) { // 最小值不能比最大值大啊 103 throw "the min value can't be greater than the max value." 104 }; 105 this.isTouchStart = false; // 觸摸是否開始 106 this.step = option.step; // 步數(回彈) 107 this.inertia = this._getValue(option.inertia, true); // 默認true,開啟慣性效果 108 109 this._calculateIndex(); // 添加this.currentPage屬性,如果寫輪播的話 110 111 this.eventTarget = window; 112 if(option.bindSelf){ 113 this.eventTarget = this.element; // 默認touchmove, touchend, touchcancel綁定在 window 上的, 如果option.bindSelf為真值,則綁定到反饋觸摸的dom 114 }; 115 116 this._moveHandler = this._move.bind(this); // 函數賦值 117 // 反饋觸摸的dom只綁定了touchstart(_start), window綁定了 touchmove(_move), touchend(_end), touchcancel(_cancel)方法 118 bind(this.element, "touchstart", this._start.bind(this)); 119 bind(this.eventTarget, "touchend", this._end.bind(this)); 120 bind(this.eventTarget, "touchcancel", this._cancel.bind(this)); 121 this.eventTarget.addEventListener("touchmove", this._moveHandler, { passive: false, capture: false }); // 使用 passive 改善的滾屏性能 122 this.x1 = this.x2 = this.y1 = this.y2 = null; // start時的坐標和move時的坐標 123 }; 124 125 AlloyTouch.prototype = { 126 _getValue: function (obj, defaultValue) { // 取用戶的值還是使用默認值 127 return obj === undefined ? defaultValue : obj; 128 }, 129 _start: function (evt) { 130 this.isTouchStart = true; // 觸摸開始 131 this.touchStart.call(this, evt, this.target[this.property]); // (1. touchStart(evt, propValue)回調) 132 cancelAnimationFrame(this.tickID); // 只要觸摸就停止動畫 133 this._calculateIndex(); // 重新計算this.currentPage屬性值 134 this.startTime = new Date().getTime(); // 開始的時間戳 135 this.x1 = this.preX = evt.touches[0].pageX; // 開始前的坐標保存到x,y 和 preXY去 136 this.y1 = this.preY = evt.touches[0].pageY; 137 this.start = this.vertical ? this.preY : this.preX; // 如果監聽豎直方向則取y坐標,否則橫向方向取x坐標 138 this._firstTouchMove = true; // 開始move(這個條件為_move做鋪墊) 139 this._preventMove = false; // 不阻止dom繼續運動(開啟慣性運動之旅的條件之一 哈哈) 140 }, 141 _move: function (evt) { 142 if (this.isTouchStart) { // 觸摸開始了 143 var len = evt.touches.length, // 手指數量 144 currentX = evt.touches[0].pageX, // move時的坐標 145 currentY = evt.touches[0].pageY; 146 147 if (this._firstTouchMove && this.lockDirection) { // 開始move 且 鎖定方向 148 var dDis = Math.abs(currentX - this.x1) - Math.abs(currentY - this.y1); // 是左右滑動還是上下滑動(x>y為水平, y>x為豎直) 149 if (dDis > 0 && this.vertical) { // 左右滑動 且 監聽豎直方向 150 this._preventMove = true; // 阻止dom繼續運動 151 } else if (dDis < 0 && !this.vertical) { // 豎直滑動 且 監聽橫向方向 152 this._preventMove = true; 153 }; // 以上2種情況直接不開啟慣性運動之旅(因為左右滑動的話this.vertical需為false,豎直滑動的話this.vertical需為true) 154 this._firstTouchMove = false; // 變成false, 為了手指連續移動中,此方法就不用進來了 155 }; 156 if(!this._preventMove) { // move時 屬性運動(關閉慣性運動后, 其實只有此運動了, 手指移動才會運動, 離開則不會運動了) 157 158 var d = (this.vertical ? currentY - this.preY : currentX - this.preX) * this.sensitivity; // 根據豎直還是左右來確定差值 * 靈敏度 159 var f = this.moveFactor; // 變量f的值為 move時的運動系數(默認1) 160 if (this.hasMax && this.target[this.property] > this.max && d > 0) { // 有最大值 且 運動屬性值>最大值 且 坐標差值d>0 161 f = this.outFactor; 162 } else if (this.hasMin && this.target[this.property] < this.min && d < 0) { // 有最小值 且 運動屬性值<最小值 且 坐標差值d<0 163 f = this.outFactor; // 滿足以上2中條件 變量f 的值就變成 this.outFactor(默認0.3) 164 }; 165 d *= f; // 坐標差值再乘以運動系數 166 this.preX = currentX; // 把move時的坐標保存到preXY去 167 this.preY = currentY; 168 if (!this.fixed) { // this.fixed默認false(fixed一旦固定了,move時, dom也不會運動) 169 this.target[this.property] += d; //把坐標的差值且乘以運動系數后的結果累加給運動的對象(被transform.js加工后的dom對象) 170 // console.log('_move: ' + this.target[this.property]); 171 }; 172 this.change.call(this, this.target[this.property]); // (2. move時的change(evt, propValue)回調) 173 var timestamp = new Date().getTime(); // move時的時間戳 174 if (timestamp - this.startTime > 300) { // move時的時間戳和start時的時間戳大於300的話 175 this.startTime = timestamp; // move時的時間戳賦值給start時的時間戳 176 this.start = this.vertical ? this.preY : this.preX; // 重新計算this.start值 177 }; 178 this.touchMove.call(this, evt, this.target[this.property]); // (3. touchMove(evt, propValue)回調) 179 }; 180 181 if (this.preventDefault && !preventDefaultTest(evt.target, this.preventDefaultException)) { //阻止默認事件除了INPUT|TEXTAREA|BUTTON|SELECT這幾個標簽 182 evt.preventDefault(); 183 }; 184 185 if (len === 1) { // 一根手指 186 if (this.x2 !== null) { //一開始為null 187 evt.deltaX = currentX - this.x2; // move移動時的差值 188 evt.deltaY = currentY - this.y2; 189 190 } else { 191 evt.deltaX = 0; // 一開始差值為0啦 192 evt.deltaY = 0; 193 } 194 this.pressMove.call(this, evt, this.target[this.property]); // (4. pressMove(evt, propValue)回調) 195 } 196 this.x2 = currentX; //把本次坐標賦值給x2,y2 197 this.y2 = currentY; 198 } 199 }, 200 _end: function (evt) { 201 if (this.isTouchStart) { // 觸摸開始了 202 203 this.isTouchStart = false; // 觸摸開始變量置為false(_move方法進不去了) 204 var self = this, // 存個實例 205 current = this.target[this.property], // 當前運動對象的運動屬性的值 206 triggerTap = (Math.abs(evt.changedTouches[0].pageX - this.x1) < 30 && Math.abs(evt.changedTouches[0].pageY - this.y1) < 30); // 是否觸發tap事件回調 207 if (triggerTap) { // 觸發tap事件 208 this.tap.call(this, evt, current); // (5. tap(evt, propValue)回調) 209 }; 210 211 if (this.touchEnd.call(this, evt, current, this.currentPage) === false) return; // (6. touchEnd(evt, propValue, 當前第幾頁)回調)這個主要給輪播用的吧 212 213 if (this.hasMax && current > this.max) { // 有最大值 且 當前運動對象的運動屬性的值大於最大值 214 215 this._to(this.max, 200, ease, this.change, function (value) { // (最大小值, time, 曲線, change函數, fn) 216 this.reboundEnd.call(this, value); 217 this.animationEnd.call(this, value); 218 }.bind(this)); 219 220 } else if (this.hasMin && current < this.min) { // 有最小值 且 當前運動對象的運動屬性的值小於最小值 221 222 this._to(this.min, 200, ease, this.change, function (value) { 223 this.reboundEnd.call(this, value); 224 this.animationEnd.call(this, value); 225 }.bind(this)); 226 227 } else if (this.inertia && !triggerTap && !this._preventMove) { // 開啟慣性效果(默認為true) 且 不觸發tap事件 且 this._preventMove為false; 228 229 var dt = new Date().getTime() - this.startTime; // _end時的時間戳和_move時的時間戳的差值 230 if (dt < 300) { // 小於300ms就執行慣性運動 231 var distance = ((this.vertical ? evt.changedTouches[0].pageY : evt.changedTouches[0].pageX) - this.start) * this.sensitivity, // _end中的坐標與_move中坐標的差值乘以靈敏度 232 speed = Math.abs(distance) / dt, // 速度為坐標差值/時間戳差值 233 speed2 = this.factor * speed; // 速度2為 系數(默認1)乘以速度 234 if(this.hasMaxSpeed && speed2 > this.maxSpeed) { // 有最大速度 且 速度2大於最大速度 235 speed2 = this.maxSpeed; // 速度2就為最大速度 236 }; 237 238 // 目標值destination = 當前運動對象的運動屬性的值 + (速度2*速度2)/(2*減速系數)*(-1||1); 239 var destination = current + (speed2 * speed2) / (2 * this.deceleration) * (distance < 0 ? -1 : 1); 240 // console.log('distance: '+ distance); 241 // console.log('目標值destination: '+ destination); 242 // console.log('差值: '+ destination > current); 243 244 var tRatio = 1; // 比例 245 if (destination < this.min ) { // 目標值 比 最小值 小 246 if (destination < this.min - this.maxRegion) { 247 tRatio = reverseEase((current - this.min + this.springMaxRegion) / (current - destination)); 248 destination = this.min - this.springMaxRegion; 249 } else { 250 tRatio = reverseEase((current - this.min + this.springMaxRegion * (this.min - destination) / this.maxRegion) / (current - destination)); 251 destination = this.min - this.springMaxRegion * (this.min - destination) / this.maxRegion; 252 } 253 } else if (destination > this.max) { // 目標值 比 最大值 大 254 if (destination > this.max + this.maxRegion) { 255 tRatio = reverseEase((this.max + this.springMaxRegion - current) / (destination - current)); 256 destination = this.max + this.springMaxRegion; 257 } else { 258 tRatio = reverseEase((this.max + this.springMaxRegion * ( destination-this.max) / this.maxRegion - current) / (destination - current)); 259 destination = this.max + this.springMaxRegion * (destination - this.max) / this.maxRegion; 260 261 } 262 }; 263 264 // 持續時間duration = 數字舍入(速度/減速系數) * 比例; 265 var duration = Math.round(speed / self.deceleration) * tRatio; 266 // console.log('持續時間duration: ' + duration); 267 268 // end方法計算好的目標值和持續時間傳入_to方法,運動起來吧 269 self._to(Math.round(destination), duration, ease, self.change, function (value) { // 回調函數的value 就是 destination 270 271 if (self.hasMax && self.target[self.property] > self.max) { // 有最大值 且 運動屬性的值大於最大值 272 273 cancelAnimationFrame(self.tickID); 274 self._to(self.max, 600, ease, self.change, self.animationEnd); 275 276 } else if (self.hasMin && self.target[self.property] < self.min) { // 有最小值 且 運動屬性的值小於最小值 277 278 cancelAnimationFrame(self.tickID); 279 self._to(self.min, 600, ease, self.change, self.animationEnd); 280 281 } else { 282 self._correction(); // 回彈 283 }; 284 285 self.change.call(this, value); // (7. change(運動屬性的值)回調函數) 286 }); 287 } else { 288 self._correction(); // 回彈 289 } 290 } else { 291 self._correction(); // 回彈 292 }; 293 294 // 阻止默認事件 295 if (this.preventDefault && !preventDefaultTest(evt.target, this.preventDefaultException)) { 296 evt.preventDefault(); 297 }; 298 299 }; 300 // 坐標置null 301 this.x1 = this.x2 = this.y1 = this.y2 = null; 302 }, 303 // 提供目標值, 持續時間, 然后根據時間戳和time持續時間的差值比較, 時間戳< time的話就一直調用動畫,否則結束 304 _to: function (value, time, ease, onChange, onEnd) { // value:目標值, time:持續時間, ease: 曲線動畫, onChange: this.change回調函數(用戶的), onEnd回調 305 if (this.fixed) return; // fixed(默認false)有真值就return掉 306 var el = this.target, // 運動的對象 307 property = this.property; // 運動的屬性 308 var current = el[property]; // 運動對象運動屬性當前的值 309 var dv = value - current; // 目標值與當前屬性的差值 310 var beginTime = new Date(); // 開始時間戳 311 var self = this; // 存個實例 312 var toTick = function () { 313 314 var dt = new Date() - beginTime; // 時間戳差值 315 if (dt >= time) { // 時間戳差值大於持續時間 316 el[property] = value; // 把目標值賦值給dom屬性 317 onChange && onChange.call(self, value); // (7. change(目標值)回調函數) 318 onEnd && onEnd.call(self, value); // onEnd回調 319 return; 320 }; 321 el[property] = dv * ease(dt / time) + current; 322 // console.log(el[property]); 323 self.tickID = requestAnimationFrame(toTick); // 動畫自調用 324 onChange && onChange.call(self, el[property]); //(7. change(屬性值)回調函數) 325 }; 326 toTick(); // 調用 327 }, 328 // 該函數用來當動畫完成后根據this.step修正一點(回彈效果) 329 _correction: function () { 330 if (this.step === undefined) return; // step沒賦值的話就return掉 331 var el = this.target, // 運動的對象 332 property = this.property; // 運動對象的運動屬性 333 var value = el[property]; // 運動對象運動屬性的值 334 var rpt = Math.floor(Math.abs(value / this.step)); // 向下取整(取絕對值(運動對象運動屬性的值/ this.step值)) 335 var dy = value % this.step; // 運動對象運動屬性的值取余數 336 337 if (Math.abs(dy) > this.step / 2) { // 我想這里又應用了啥物理原理根據條件判斷,來計算value目標值的,然后調用_to方法執行慣性運動 338 this._to((value < 0 ? -1 : 1) * (rpt + 1) * this.step, 400, ease, this.change, function (value) { 339 this._calculateIndex(); 340 this.correctionEnd.call(this, value); 341 this.animationEnd.call(this, value); 342 }.bind(this)); 343 } else { 344 this._to((value < 0 ? -1 : 1) * rpt * this.step, 400, ease, this.change, function (value) { 345 this._calculateIndex(); // 重新計算this.currentPage值 346 this.correctionEnd.call(this, value); // (8. correctionEnd(屬性值)回調函數) 347 this.animationEnd.call(this, value); // (9. animationEnd(屬性值)回調函數) 348 }.bind(this)); 349 } 350 }, 351 _cancel: function (evt) { 352 var current = this.target[this.property]; 353 this.touchCancel.call(this, evt, current); 354 this._end(evt); 355 }, 356 // 給用戶使用的, 控制dom以不同的曲線動畫運動 357 to: function (v, time, user_ease) { 358 this._to(v, this._getValue(time, 600), user_ease || ease, this.change, function (value) { 359 this._calculateIndex(); 360 this.reboundEnd.call(this, value); // (10. reboundEnd(屬性值)回調函數) 361 this.animationEnd.call(this, value); // (9. animationEnd(屬性值)回調函數) 362 }.bind(this)); 363 364 }, 365 // 計算this.currentPage值 366 _calculateIndex: function () { 367 if (this.hasMax && this.hasMin) { 368 this.currentPage = Math.round((this.max - this.target[this.property]) / this.step); // 當前第幾頁,比如輪播圖的第幾個,從0開始 369 } 370 } 371 372 }; 373 374 // 拋出去 375 if (typeof module !== 'undefined' && typeof exports === 'object') { 376 module.exports = AlloyTouch; 377 } else { 378 window.AlloyTouch = AlloyTouch; 379 }; 380 381 })();
使用姿勢:(由於此庫比較抽象,使用姿勢需要根據需求來使用,以下提供了3個demo的使用姿勢,基本差不多,可以用手機掃二維碼看看(由於編寫html頁面時,沒考慮手機屏幕的適應,所以湊合這點吧,哈哈))
1 // es5 語法 2 // 列表加載更多 3 var List = function() { 4 this.list_Target = document.querySelector("#list_target"); 5 this.Ul = this.list_Target.children[0]; 6 this.oList = document.querySelector("#list"); 7 //給element注入transform屬性 8 Transform(this.list_Target); 9 10 this.at = null; 11 this.loading = false; 12 this.index = 21; 13 }; 14 15 Object.assign(List.prototype, { 16 init: function() { 17 var self = this; 18 this.at = new AlloyTouch({ 19 touch: '#list',//反饋觸摸的dom 20 vertical: true,//不必需,默認是true代表監聽豎直方向touch 21 target: this.list_Target, //運動的對象 22 property: "translateY", //被滾動的屬性 23 factor: 1,//不必需,默認值是1代表touch區域的1px的對應target.y的1 24 inertia: true, 25 min: this.oList.offsetHeight - this.list_Target.offsetHeight, //不必需,滾動屬性的最小值 26 max: 0, //不必需,滾動屬性的最大值 27 step: 2, 28 touchStart: function() { 29 self.getMin(); 30 }, 31 change: function(v) { 32 // console.log(v); 33 if (v < this.min && !self.loading) { 34 self.loading = true; 35 self.loadMore(); 36 }; 37 } 38 }); 39 }, 40 getMin: function() { 41 this.at.min = -parseInt(getComputedStyle(this.list_Target).height) + this.oList.offsetHeight; 42 }, 43 loadMore: function() { 44 setTimeout(function() { 45 this.loading = false; 46 var Oli = null, 47 i = 0, 48 len = 20; 49 50 for (; i < len; i ++) { 51 this.index += 1; 52 Oli = document.createElement('li'); 53 Oli.innerHTML = this.index; 54 this.Ul.appendChild(Oli); // 這里測試,不推薦這么寫啊, 太消耗性能了 55 }; 56 57 this.getMin(); 58 }.bind(this), 500); 59 } 60 }); 61 62 new List().init(); 63 64 // es6 語法 65 /*let flower = (new class { 66 constructor() { 67 this.at = null; 68 this.target = this.$('testImg'); 69 Transform(this.target); 70 } 71 72 $(id) { 73 return document.querySelector('#' + id); 74 } 75 76 init() { 77 this.at = new AlloyTouch({ 78 touch: this.target,//反饋觸摸的dom 79 vertical: false,//不必需,默認是true代表監聽豎直方向touch 80 target: this.target, //運動的對象 81 property: "rotateY", //被滾動的屬性 82 factor: 1,//不必需,默認值是1代表touch區域的1px的對應target.y的1 83 inertia: true, 84 step: 100 85 }); 86 } 87 }).init();*/ 88 89 90 // 花朵 91 var flower = function() { 92 93 var target = $('testImg'); 94 Transform(target); 95 96 function $(id) { 97 return document.querySelector('#' + id); 98 }; 99 100 return function() { 101 return new AlloyTouch({ 102 touch: target,//反饋觸摸的dom 103 vertical: false,//不必需,默認是true代表監聽豎直方向touch 104 target: target, //運動的對象 105 property: "rotateY", //被滾動的屬性 106 factor: 1,//不必需,默認值是1代表touch區域的1px的對應target.y的1 107 inertia: true, 108 step: 100 109 }); 110 }; 111 }()(); 112 113 114 // 輪播 115 var carousel = function() { 116 var scroller = document.querySelector('#carousel_scroller'); 117 Transform(scroller); 118 var aA = document.querySelectorAll('#nav a'); 119 var at = null; 120 var tickId = null; 121 122 function init() { 123 at = new AlloyTouch({ 124 touch: '#carousel_container', 125 target: scroller, 126 vertical: false, 127 property: 'translateX', 128 step: window.innerWidth, 129 max: 0, 130 min: - window.innerWidth * 3, 131 touchStart: function() { 132 clearInterval(tickId); 133 }, 134 touchEnd: function(evt, v, index) { 135 var value = -(this.step * index); 136 var dt = v - value; 137 console.log(dt); 138 139 if (v > this.max) { // 屬性值大於最大值取最大值 140 this.to(this.max); 141 } else if (v < this.min) { // 屬性值小於最小值取最小值 142 this.to(this.min); 143 } else if (Math.abs(dt) < 30) { // 2邊空隙小於30就回到當前值 144 this.to(value); 145 } else if (dt > 0) { // 大於0往右邊滾動一個 146 this.to(value + this.step); 147 } else { // 小於0就往左邊滾動一個 148 this.to(value - this.step); 149 }; 150 loop(); 151 return false; 152 }, 153 animationEnd: function(evt, v) { 154 Array.prototype.slice.call(aA).forEach(function(item, index) { 155 item.className = ''; 156 }); 157 aA[this.currentPage].className = 'active'; 158 } 159 }); 160 }; 161 162 // 循環播放 163 function loop() { 164 tickId = setInterval(function() { 165 this.currentPage += 1; 166 if (this.currentPage > 3) { 167 this.currentPage = 0; 168 }; 169 this.to(-(this.currentPage * this.step)); 170 }.bind(at), 2000); 171 }; 172 173 return { 174 init: init, 175 loop: loop 176 }; 177 }(); 178 carousel.init(); 179 carousel.loop();
我覺得還是用手機體驗一把alloyTouch的靈活和強大吧,手機對准掃一掃吧。
3個關於alloyTouch的小demo。
更多demo,請去github看看。
ps:
此庫還是很有用的,很喜歡。
github: https://github.com/AlloyTeam/AlloyTouch