move.js 源码 学习笔记

源码笔记:

  1 /* move.js
  2  * @author:flfwzgl https://github.com/flfwzgl
  3  * @copyright: MIT license 
  4  * Sorrow.X --- 添加注释,注释纯属个人理解(源码有稍微改动,方便阅读)
  5  * */
  6 
  7 ! function() {
  8 
  9     var PI = Math.PI,
 10         sin = Math.sin,
 11         cos = Math.cos,
 12         pow = Math.pow,
 13         abs = Math.abs,
 14         sqrt = Math.sqrt;
 15 
 16     var request = window.requestAnimationFrame,
 17         stopRequest = window.cancelAnimationFrame;
 18     var _move, _stopMove;    // 都是函数, 不支持requestAnimationFrame的浏览器就使用定时器
 19 
 20     //初始化运动函数和停止函数  
 21     if (request) {
 22         _move = function(fn, timer) {    // fn: 匿名函数, timer: 不同的空对象
 23             var step = function() {
 24                 if (!fn()) {    // fn函数返回值为假值则调用requestAnimationFrame方法(true代表运动结束)
 25                     timer.id = request(step);
 26                 };
 27             };
 28             step();    // 函数调用
 29         };
 30     } else {
 31         _move = function(fn, timer) {
 32             timer.id = setInterval(fn, 16);    // 采用定时器, 时间间隔不能低于16
 33         };
 34     };
 35     if (stopRequest) {
 36         _stopMove = function(timer) {
 37             stopRequest(timer.id);    // 停止动画调用
 38         };
 39     } else {
 40         _stopMove = function(timer) {
 41             clearInterval(timer.id);    // 关闭定时器
 42         };
 43     };
 44 
 45     var Move = function() {};    // Move构造函数
 46 
 47     var curve = Move.prototype = {    // Move原型
 48         extend: function(obj) {
 49             for (var k in obj) {
 50                 if (k in curve) {
 51                     console.warn('扩张的方法名' + k + ': 已经存在, 换个方法名吧!' );
 52                     return;
 53                 };
 54                 curve[k] = (function(moveType) {    // 给Move原型添加 动画曲线 方法
 55                     return function() {
 56                         return _doMove.call(this, arguments, moveType);    // 每个动画曲线方法实际调用_doMove函数
 57                     };
 58                 })(obj[k]);
 59             };
 60         }
 61     };
 62 
 63     /*****  动画曲线  ******/
 64     curve.extend({
 65         //定义域和值域均为[0, 1], 传入自变量x返回对应值y
 66         //先加速后减速
 67         ease: function(x) {
 68             // return -0.5*cos(PI * (2 - x)) + 0.5;
 69             if (x <= 0.5) return 2 * x * x;
 70             else if (x > 0.5) return -2 * x * x + 4 * x - 1;
 71         },
 72 
 73         // 初速度为0 ,一直加速
 74         easeIn: function(x) {
 75             return x * x;
 76         },
 77 
 78         //先慢慢加速1/3, 然后突然大提速, 最后减速
 79         ease2: function(x) {
 80             return x < 1 / 3 ? x * x : -2 * x * x + 4 * x - 1;
 81         },
 82 
 83         //初速度较大, 一直减速, 缓冲动画
 84         easeOut: function(x) {
 85             return pow(x, 0.8);
 86         },
 87 
 88         //碰撞动画
 89         collision: function(x) {
 90             var a, b; //a, b代表碰撞点的横坐标
 91             for (var i = 1, m = 20; i < m; i++) {
 92                 a = 1 - (4 / 3) * pow(0.5, i - 1);
 93                 b = 1 - (4 / 3) * pow(0.5, i);
 94                 if (x >= a && x <= b) {
 95                     return pow(3 * (x - (a + b) / 2), 2) + 1 - pow(0.25, i - 1);
 96                 }
 97             }
 98         },
 99 
100         //弹性动画
101         elastic: function(x) {
102             return -pow(1 / 12, x) * cos(PI * 2.5 * x * x) + 1;
103         },
104 
105         //匀速动画
106         linear: function(x) {
107             return x;
108         },
109 
110         //断断续续加速减速
111         wave: function(x) {
112             return (1 / 12) * sin(5 * PI * x) + x;
113         },
114 
115         //先向反方向移动一小段距离, 然后正方向移动, 并超过终点一小段, 然后回到终点
116         opposite: function(x) {
117             return (sqrt(2) / 2) * sin((3 * PI / 2) * (x - 0.5)) + 0.5;
118         },
119 
120         // 相反的三次贝塞尔
121         reverseEase: function (x) {    
122             return 1 - Math.sqrt(1 - x * x);
123         }
124     });
125 
126     /**
127      * 开始动画函数
128      * arg: 用户要传的([0, 1000], 500, function(v){ ... }, fnEnd)
129      * moveType: 曲线动画函数
130      */
131     function _doMove(arg, moveType) {
132         var r,    // r => 过渡范围, 例如[0, 1000]   (必须传, 且传数组)
133             d,    // d => 过渡时间, ms,             (可不传, 默认500) 
134             fn,    // fn => 每一帧的回调函数, 传入当前过渡值v   (必须传) 
135             fnEnd;    // fnEnd => 动画结束时回调               (可不传)    
136 
137         // 严格限制传入参数, 且传入的参数可以没有顺序
138         for (var i = 0; i < 4; i++) {
139             if (typeof arg[i] === 'object' && !r) r = arg[i];
140             else if (typeof arg[i] === 'number' && !d) d = arg[i];
141             else if (typeof arg[i] === 'function' && !fn) fn = arg[i];
142             else if (typeof arg[i] === 'function' && !fnEnd) fnEnd = arg[i];
143         };
144 
145         if (!r instanceof Array || !fn) return;    // 如果r不是数组或者fn不是函数(真值)就return掉
146 
147         d = d || 500;    // 过渡时间默认500ms
148 
149         var from = +new Date, //起始时间
150             x = 0,    
151             y,
152             a = r[0],    // 过渡范围的起点
153             b = r[1];    // 过度范围的终点
154 
155         var timer = 't' + Math.random();    // 随机数
156 
157         var self = this;    // 存一下Move的实例
158 
159         //用于保存定时器ID的对象, requestAnimation递归调用必须传入对象(给实例添加timer属性值为{})
160         this[timer] = {};
161 
162         // 优先使用requestAnimationFrame否则setInterval定时器
163         _move(function() {
164             x = (+new Date - from) / d;
165 
166             if (x >= 1) {    // 动画结束
167                 fn(b);    // 调用外部动画的回调函数且把过度范围的终点值作为参数传过去
168                 if (fnEnd) fnEnd();    // 如果有动画结束回调函数就执行回调函数
169                 return true;    // 返回真值停止调用requestAnimationFrame方法
170             } else {    // 动画进行中
171                 y = moveType(x);    // 调用动画曲线中的函数返回运动数字
172                 fn(a + (b - a) * y);    // 调用外部动画的回调函数传参为 a + (b - a) * y
173             };
174         }, self[timer]);
175 
176         return function() {
177             _stopMove(self[timer]);    // 调用cancelAnimationFrame方法停止动画
178             return a + (b - a) * y;    // 返回动画停止后的运动数字
179         };
180     };
181 
182     // 抛出去
183     if (typeof module === 'object' && module.exports) {
184         module.exports = new Move;
185     } else {
186         if (window.move) {
187             try {
188                 console.warn('move has been declared!');
189             } catch (e) {};
190         } else {
191             window.move = new Move;    // 抛出去的是个Move实例
192         }
193     };
194 }();

 

使用姿势:

 

        var box = document.querySelector("#box");

        // 扩展运动函数
        move.extend({
            fast: function(x) {
                return x * x * x;
            },
            reverseEase: function (x) {    // 相反的三次贝塞尔
                return 1 - Math.sqrt(1 - x * x);
            }
        })

        // 使用姿势
        var stop = move.reverseEase([0, 500], 1000, function(v){
            console.log(v);
            box.style.left = v + 'px';
        }, function(){
            console.log('动画完成');
        });

        setTimeout(function() {
            var val = stop();  //停止动画
            console.log('停止:' + val);
        }, 500);

 

个人喜好,我改成了自己喜欢的源码,添加了随机取动画名字:

  1 ; (function() {
  2 
  3     var PI = Math.PI,
  4         sin = Math.sin,
  5         cos = Math.cos,
  6         pow = Math.pow,
  7         abs = Math.abs,
  8         sqrt = Math.sqrt;
  9 
 10     var request = window.requestAnimationFrame,
 11         stopRequest = window.cancelAnimationFrame;
 12     var _move, _stopMove;    // 都是函数, 不支持requestAnimationFrame的浏览器就使用定时器
 13 
 14     //初始化运动函数和停止函数  
 15     if (request) {
 16         _move = function(fn, timer) {    // fn: 匿名函数, timer: 不同的空对象
 17             var step = function() {
 18                 if (!fn()) {    // fn函数返回值为假值则调用requestAnimationFrame方法(true代表运动结束)
 19                     timer.id = request(step);
 20                 };
 21             };
 22             step();    // 函数调用
 23         };
 24     } else {
 25         _move = function(fn, timer) {
 26             timer.id = setInterval(fn, 16);    // 采用定时器, 时间间隔不能低于16
 27         };
 28     };
 29     if (stopRequest) {
 30         _stopMove = function(timer) {
 31             stopRequest(timer.id);    // 停止动画调用
 32         };
 33     } else {
 34         _stopMove = function(timer) {
 35             clearInterval(timer.id);    // 关闭定时器
 36         };
 37     };
 38 
 39     var Move = function() {    // Move构造函数
 40         this.aCurve = [];    // 曲线动画函数名集合
 41         this.init();
 42     };    
 43 
 44 
 45     var curve = Move.prototype = {    // Move原型
 46         // 初始化动画曲线 
 47         init: function() {
 48             this.extends({
 49                 //定义域和值域均为[0, 1], 传入自变量x返回对应值y
 50                 //先加速后减速
 51                 ease: function(x) {
 52                     // return -0.5*cos(PI * (2 - x)) + 0.5;
 53                     if (x <= 0.5) return 2 * x * x;
 54                     else if (x > 0.5) return -2 * x * x + 4 * x - 1;
 55                 },
 56 
 57                 // 初速度为0 ,一直加速
 58                 easeIn: function(x) {
 59                     return x * x;
 60                 },
 61 
 62                 //先慢慢加速1/3, 然后突然大提速, 最后减速
 63                 ease2: function(x) {
 64                     return x < 1 / 3 ? x * x : -2 * x * x + 4 * x - 1;
 65                 },
 66 
 67                 //初速度较大, 一直减速, 缓冲动画
 68                 easeOut: function(x) {
 69                     return pow(x, 0.8);
 70                 },
 71 
 72                 //碰撞动画
 73                 collision: function(x) {
 74                     var a, b; //a, b代表碰撞点的横坐标
 75                     for (var i = 1, m = 20; i < m; i++) {
 76                         a = 1 - (4 / 3) * pow(0.5, i - 1);
 77                         b = 1 - (4 / 3) * pow(0.5, i);
 78                         if (x >= a && x <= b) {
 79                             return pow(3 * (x - (a + b) / 2), 2) + 1 - pow(0.25, i - 1);
 80                         }
 81                     }
 82                 },
 83 
 84                 //弹性动画
 85                 elastic: function(x) {
 86                     return -pow(1 / 12, x) * cos(PI * 2.5 * x * x) + 1;
 87                 },
 88 
 89                 //匀速动画
 90                 linear: function(x) {
 91                     return x;
 92                 },
 93 
 94                 //断断续续加速减速
 95                 wave: function(x) {
 96                     return (1 / 12) * sin(5 * PI * x) + x;
 97                 },
 98 
 99                 //先向反方向移动一小段距离, 然后正方向移动, 并超过终点一小段, 然后回到终点
100                 opposite: function(x) {
101                     return (sqrt(2) / 2) * sin((3 * PI / 2) * (x - 0.5)) + 0.5;
102                 },
103 
104                 // 相反的三次贝塞尔
105                 reverseEase: function (x) {    
106                     return 1 - Math.sqrt(1 - x * x);
107                 }
108             });
109         },
110 
111         // 随机选择一个动画方法名
112         getRd: function () {
113             var preItem = null;
114 
115             return function () {
116                 var arr = this.aCurve;
117                 var index = Math.floor(Math.random() * arr.length),
118                     item = arr[index],
119                     result;
120 
121                 if (preItem != item) {
122                     preItem = item;
123                     result = item;
124                 } else {
125                     result = this.getRd(arr);
126                 };
127 
128                 return result;
129             };
130         }(),
131 
132         // 扩张曲线动画
133         extends: function(obj) {
134             for (var k in obj) {
135                 if (k in curve) {
136                     console.warn('扩张的方法名' + k + ': 已经存在, 换个方法名吧!' );
137                     return;
138                 };
139                 this.aCurve.push(k);
140                 curve[k] = (function(moveType) {    // 给Move原型添加 动画曲线 方法
141                     return function() {
142                         return _doMove.call(this, arguments, moveType);    // 每个动画曲线方法实际调用_doMove函数
143                     };
144                 })(obj[k]);
145             };
146         }
147     };
148 
149 
150     /**
151      * 开始动画函数
152      * arg: 用户要传的([0, 1000], 500, function(v){ ... }, fnEnd)
153      * moveType: 曲线动画函数
154      */
155     function _doMove(arg, moveType) {
156         var r,    // r => 过渡范围, 例如[0, 1000]   (必须传, 且传数组)
157             d,    // d => 过渡时间, ms,             (可不传, 默认500) 
158             fn,    // fn => 每一帧的回调函数, 传入当前过渡值v   (必须传) 
159             fnEnd;    // fnEnd => 动画结束时回调               (可不传)    
160 
161         // 严格限制传入参数, 且传入的参数可以没有顺序
162         for (var i = 0; i < 4; i++) {
163             if (typeof arg[i] === 'object' && !r) r = arg[i];
164             else if (typeof arg[i] === 'number' && !d) d = arg[i];
165             else if (typeof arg[i] === 'function' && !fn) fn = arg[i];
166             else if (typeof arg[i] === 'function' && !fnEnd) fnEnd = arg[i];
167         };
168 
169         if (!r instanceof Array || !fn) return;    // 如果r不是数组或者fn不是函数(真值)就return掉
170 
171         d = d || 500;    // 过渡时间默认500ms
172 
173         var from = +new Date, //起始时间
174             x = 0,    
175             y,
176             a = r[0],    // 过渡范围的起点
177             b = r[1];    // 过度范围的终点
178 
179         var timer = 't' + Math.random();    // 随机数
180 
181         var self = this;    // 存一下Move的实例
182 
183         //用于保存定时器ID的对象, requestAnimation递归调用必须传入对象(给实例添加timer属性值为{})
184         this[timer] = {};
185 
186         // 优先使用requestAnimationFrame否则setInterval定时器
187         _move(function() {
188             x = (+new Date - from) / d;
189 
190             if (x >= 1) {    // 动画结束
191                 fn(b);    // 调用外部动画的回调函数且把过度范围的终点值作为参数传过去
192                 if (fnEnd) fnEnd();    // 如果有动画结束回调函数就执行回调函数
193                 return true;    // 返回真值停止调用requestAnimationFrame方法
194             } else {    // 动画进行中
195                 y = moveType(x);    // 调用动画曲线中的函数返回运动数字
196                 fn(a + (b - a) * y);    // 调用外部动画的回调函数传参为 a + (b - a) * y
197             };
198         }, self[timer]);
199 
200         return function() {
201             _stopMove(self[timer]);    // 调用cancelAnimationFrame方法停止动画
202             return a + (b - a) * y;    // 返回动画停止后的运动数字
203         };
204     };
205 
206     // 抛出去
207     if (typeof module === 'object' && module.exports) {
208         module.exports = Move;
209     } else {
210         if (window.Move) {
211             try {
212                 console.warn('Move has been declared!');
213             } catch (e) {};
214         } else {
215             window.Move = Move;    // Move构造函数抛出去
216         }
217     };
218 })();

使用姿势:

        var move = new Move();
        
        move.extends({    // 自己自定义扩展
            fast: function(x) {
                return x * x * x;
            },
            reverseEase: function (x) {    // 相反的三次贝塞尔
                return 1 - Math.sqrt(1 - x * x);
            }
        })

        document.querySelector('#btn').addEventListener('click', function() {
            // 使用姿势
            var rd = move.getRd();
            var stop = move[rd]([0, 500], 1000, function(v){
                console.log(v);
                box.style.left = v + 'px';
            }, function(){
                console.log('动画完成');
            });

            setTimeout(function() {
                var val = stop();  //停止动画
                console.log('停止:' + val);
            }, 500);
        }, false);

 

ps:

     很喜欢的一个数字运动小型库,便于扩张各种运动。配合dnt的transform.js,真的很不错。

     很多ios和安卓的前端动画都能轻松写出来。

     比jq提供的动画更加强大和灵活。

     我只是个搬运工,喜欢的库自然会贴出原作者地址。

     看原作者怎么说明的:https://github.com/flfwzgl/move

posted @ 2017-03-02 14:24  Sorrow.X  阅读(1303)  评论(0编辑  收藏  举报