AlloyTouch.js 源碼 學習筆記及原理說明


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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM