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

开心的做一个无忧无虑的码农,争取每天进步一点。
原文地址:https://www.cnblogs.com/sorrowx/p/6531273.html