move.js 源码 学习笔记
源码笔记:
/* move.js
* @author:flfwzgl https://github.com/flfwzgl
* @copyright: MIT license
* Sorrow.X --- 添加注释,注释纯属个人理解(源码有稍微改动,方便阅读)
* */ ! function() { var PI = Math.PI,
sin = Math.sin,
cos = Math.cos,
pow = Math.pow,
abs = Math.abs,
sqrt = Math.sqrt; var request = window.requestAnimationFrame,
stopRequest = window.cancelAnimationFrame;
var _move, _stopMove; // 都是函数, 不支持requestAnimationFrame的浏览器就使用定时器 //初始化运动函数和停止函数
if (request) {
_move = function(fn, timer) { // fn: 匿名函数, timer: 不同的空对象
var step = function() {
if (!fn()) { // fn函数返回值为假值则调用requestAnimationFrame方法(true代表运动结束)
timer.id = request(step);
};
};
step(); // 函数调用
};
} else {
_move = function(fn, timer) {
timer.id = setInterval(fn, 16); // 采用定时器, 时间间隔不能低于16
};
};
if (stopRequest) {
_stopMove = function(timer) {
stopRequest(timer.id); // 停止动画调用
};
} else {
_stopMove = function(timer) {
clearInterval(timer.id); // 关闭定时器
};
}; var Move = function() {}; // Move构造函数 var curve = Move.prototype = { // Move原型
extend: function(obj) {
for (var k in obj) {
if (k in curve) {
console.warn('扩张的方法名' + k + ': 已经存在, 换个方法名吧!' );
return;
};
curve[k] = (function(moveType) { // 给Move原型添加 动画曲线 方法
return function() {
return _doMove.call(this, arguments, moveType); // 每个动画曲线方法实际调用_doMove函数
};
})(obj[k]);
};
}
}; /***** 动画曲线 ******/
curve.extend({
//定义域和值域均为[0, 1], 传入自变量x返回对应值y
//先加速后减速
ease: function(x) {
// return -0.5*cos(PI * (2 - x)) + 0.5;
if (x <= 0.5) return 2 * x * x;
else if (x > 0.5) return -2 * x * x + 4 * x - 1;
}, // 初速度为0 ,一直加速
easeIn: function(x) {
return x * x;
}, //先慢慢加速1/3, 然后突然大提速, 最后减速
ease2: function(x) {
return x < 1 / 3 ? x * x : -2 * x * x + 4 * x - 1;
}, //初速度较大, 一直减速, 缓冲动画
easeOut: function(x) {
return pow(x, 0.8);
}, //碰撞动画
collision: function(x) {
var a, b; //a, b代表碰撞点的横坐标
for (var i = 1, m = 20; i < m; i++) {
a = 1 - (4 / 3) * pow(0.5, i - 1);
b = 1 - (4 / 3) * pow(0.5, i);
if (x >= a && x <= b) {
return pow(3 * (x - (a + b) / 2), 2) + 1 - pow(0.25, i - 1);
}
}
}, //弹性动画
elastic: function(x) {
return -pow(1 / 12, x) * cos(PI * 2.5 * x * x) + 1;
}, //匀速动画
linear: function(x) {
return x;
}, //断断续续加速减速
wave: function(x) {
return (1 / 12) * sin(5 * PI * x) + x;
}, //先向反方向移动一小段距离, 然后正方向移动, 并超过终点一小段, 然后回到终点
opposite: function(x) {
return (sqrt(2) / 2) * sin((3 * PI / 2) * (x - 0.5)) + 0.5;
}, // 相反的三次贝塞尔
reverseEase: function (x) {
return 1 - Math.sqrt(1 - x * x);
}
}); /**
* 开始动画函数
* arg: 用户要传的([0, 1000], 500, function(v){ ... }, fnEnd)
* moveType: 曲线动画函数
*/
function _doMove(arg, moveType) {
var r, // r => 过渡范围, 例如[0, 1000] (必须传, 且传数组)
d, // d => 过渡时间, ms, (可不传, 默认500)
fn, // fn => 每一帧的回调函数, 传入当前过渡值v (必须传)
fnEnd; // fnEnd => 动画结束时回调 (可不传) // 严格限制传入参数, 且传入的参数可以没有顺序
for (var i = 0; i < 4; i++) {
if (typeof arg[i] === 'object' && !r) r = arg[i];
else if (typeof arg[i] === 'number' && !d) d = arg[i];
else if (typeof arg[i] === 'function' && !fn) fn = arg[i];
else if (typeof arg[i] === 'function' && !fnEnd) fnEnd = arg[i];
}; if (!r instanceof Array || !fn) return; // 如果r不是数组或者fn不是函数(真值)就return掉 d = d || 500; // 过渡时间默认500ms var from = +new Date, //起始时间
x = 0,
y,
a = r[0], // 过渡范围的起点
b = r[1]; // 过度范围的终点 var timer = 't' + Math.random(); // 随机数 var self = this; // 存一下Move的实例 //用于保存定时器ID的对象, requestAnimation递归调用必须传入对象(给实例添加timer属性值为{})
this[timer] = {}; // 优先使用requestAnimationFrame否则setInterval定时器
_move(function() {
x = (+new Date - from) / d; if (x >= 1) { // 动画结束
fn(b); // 调用外部动画的回调函数且把过度范围的终点值作为参数传过去
if (fnEnd) fnEnd(); // 如果有动画结束回调函数就执行回调函数
return true; // 返回真值停止调用requestAnimationFrame方法
} else { // 动画进行中
y = moveType(x); // 调用动画曲线中的函数返回运动数字
fn(a + (b - a) * y); // 调用外部动画的回调函数传参为 a + (b - a) * y
};
}, self[timer]); return function() {
_stopMove(self[timer]); // 调用cancelAnimationFrame方法停止动画
return a + (b - a) * y; // 返回动画停止后的运动数字
};
}; // 抛出去
if (typeof module === 'object' && module.exports) {
module.exports = new Move;
} else {
if (window.move) {
try {
console.warn('move has been declared!');
} catch (e) {};
} else {
window.move = new Move; // 抛出去的是个Move实例
}
};
}();
使用姿势:
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);
个人喜好,我改成了自己喜欢的源码,添加了随机取动画名字:
; (function() {
var PI = Math.PI,
sin = Math.sin,
cos = Math.cos,
pow = Math.pow,
abs = Math.abs,
sqrt = Math.sqrt;
var request = window.requestAnimationFrame,
stopRequest = window.cancelAnimationFrame;
var _move, _stopMove; // 都是函数, 不支持requestAnimationFrame的浏览器就使用定时器
//初始化运动函数和停止函数
if (request) {
_move = function(fn, timer) { // fn: 匿名函数, timer: 不同的空对象
var step = function() {
if (!fn()) { // fn函数返回值为假值则调用requestAnimationFrame方法(true代表运动结束)
timer.id = request(step);
};
};
step(); // 函数调用
};
} else {
_move = function(fn, timer) {
timer.id = setInterval(fn, 16); // 采用定时器, 时间间隔不能低于16
};
};
if (stopRequest) {
_stopMove = function(timer) {
stopRequest(timer.id); // 停止动画调用
};
} else {
_stopMove = function(timer) {
clearInterval(timer.id); // 关闭定时器
};
};
var Move = function() { // Move构造函数
this.aCurve = []; // 曲线动画函数名集合
this.init();
};
var curve = Move.prototype = { // Move原型
// 初始化动画曲线
init: function() {
this.extends({
//定义域和值域均为[0, 1], 传入自变量x返回对应值y
//先加速后减速
ease: function(x) {
// return -0.5*cos(PI * (2 - x)) + 0.5;
if (x <= 0.5) return 2 * x * x;
else if (x > 0.5) return -2 * x * x + 4 * x - 1;
},
// 初速度为0 ,一直加速
easeIn: function(x) {
return x * x;
},
//先慢慢加速1/3, 然后突然大提速, 最后减速
ease2: function(x) {
return x < 1 / 3 ? x * x : -2 * x * x + 4 * x - 1;
},
//初速度较大, 一直减速, 缓冲动画
easeOut: function(x) {
return pow(x, 0.8);
},
//碰撞动画
collision: function(x) {
var a, b; //a, b代表碰撞点的横坐标
for (var i = 1, m = 20; i < m; i++) {
a = 1 - (4 / 3) * pow(0.5, i - 1);
b = 1 - (4 / 3) * pow(0.5, i);
if (x >= a && x <= b) {
return pow(3 * (x - (a + b) / 2), 2) + 1 - pow(0.25, i - 1);
}
}
},
//弹性动画
elastic: function(x) {
return -pow(1 / 12, x) * cos(PI * 2.5 * x * x) + 1;
},
//匀速动画
linear: function(x) {
return x;
},
//断断续续加速减速
wave: function(x) {
return (1 / 12) * sin(5 * PI * x) + x;
},
//先向反方向移动一小段距离, 然后正方向移动, 并超过终点一小段, 然后回到终点
opposite: function(x) {
return (sqrt(2) / 2) * sin((3 * PI / 2) * (x - 0.5)) + 0.5;
},
// 相反的三次贝塞尔
reverseEase: function (x) {
return 1 - Math.sqrt(1 - x * x);
}
});
},
// 随机选择一个动画方法名
getRd: function () {
var preItem = null;
return function () {
var arr = this.aCurve;
var index = Math.floor(Math.random() * arr.length),
item = arr[index],
result;
if (preItem != item) {
preItem = item;
result = item;
} else {
result = this.getRd(arr);
};
return result;
};
}(),
// 扩张曲线动画
extends: function(obj) {
for (var k in obj) {
if (k in curve) {
console.warn('扩张的方法名' + k + ': 已经存在, 换个方法名吧!' );
return;
};
this.aCurve.push(k);
curve[k] = (function(moveType) { // 给Move原型添加 动画曲线 方法
return function() {
return _doMove.call(this, arguments, moveType); // 每个动画曲线方法实际调用_doMove函数
};
})(obj[k]);
};
}
};
/**
* 开始动画函数
* arg: 用户要传的([0, 1000], 500, function(v){ ... }, fnEnd)
* moveType: 曲线动画函数
*/
function _doMove(arg, moveType) {
var r, // r => 过渡范围, 例如[0, 1000] (必须传, 且传数组)
d, // d => 过渡时间, ms, (可不传, 默认500)
fn, // fn => 每一帧的回调函数, 传入当前过渡值v (必须传)
fnEnd; // fnEnd => 动画结束时回调 (可不传)
// 严格限制传入参数, 且传入的参数可以没有顺序
for (var i = 0; i < 4; i++) {
if (typeof arg[i] === 'object' && !r) r = arg[i];
else if (typeof arg[i] === 'number' && !d) d = arg[i];
else if (typeof arg[i] === 'function' && !fn) fn = arg[i];
else if (typeof arg[i] === 'function' && !fnEnd) fnEnd = arg[i];
};
if (!r instanceof Array || !fn) return; // 如果r不是数组或者fn不是函数(真值)就return掉
d = d || 500; // 过渡时间默认500ms
var from = +new Date, //起始时间
x = 0,
y,
a = r[0], // 过渡范围的起点
b = r[1]; // 过度范围的终点
var timer = 't' + Math.random(); // 随机数
var self = this; // 存一下Move的实例
//用于保存定时器ID的对象, requestAnimation递归调用必须传入对象(给实例添加timer属性值为{})
this[timer] = {};
// 优先使用requestAnimationFrame否则setInterval定时器
_move(function() {
x = (+new Date - from) / d;
if (x >= 1) { // 动画结束
fn(b); // 调用外部动画的回调函数且把过度范围的终点值作为参数传过去
if (fnEnd) fnEnd(); // 如果有动画结束回调函数就执行回调函数
return true; // 返回真值停止调用requestAnimationFrame方法
} else { // 动画进行中
y = moveType(x); // 调用动画曲线中的函数返回运动数字
fn(a + (b - a) * y); // 调用外部动画的回调函数传参为 a + (b - a) * y
};
}, self[timer]);
return function() {
_stopMove(self[timer]); // 调用cancelAnimationFrame方法停止动画
return a + (b - a) * y; // 返回动画停止后的运动数字
};
};
// 抛出去
if (typeof module === 'object' && module.exports) {
module.exports = Move;
} else {
if (window.Move) {
try {
console.warn('Move has been declared!');
} catch (e) {};
} else {
window.Move = Move; // Move构造函数抛出去
}
};
})();
使用姿势:
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
move.js 源码 学习笔记的更多相关文章
- Underscore.js 源码学习笔记(下)
上接 Underscore.js 源码学习笔记(上) === 756 行开始 函数部分. var executeBound = function(sourceFunc, boundFunc, cont ...
- Underscore.js 源码学习笔记(上)
版本 Underscore.js 1.9.1 一共 1693 行.注释我就删了,太长了… 整体是一个 (function() {...}()); 这样的东西,我们应该知道这是一个 IIFE(立即执行 ...
- Vue.js 源码学习笔记
最近饶有兴致的又把最新版 Vue.js 的源码学习了一下,觉得真心不错,个人觉得 Vue.js 的代码非常之优雅而且精辟,作者本身可能无 (bu) 意 (xie) 提及这些.那么,就让我来吧:) 程序 ...
- AlloyTouch.js 源码 学习笔记及原理说明
alloyTouch这个库其实可以做很多事的, 比较抽象, 需要我们用户好好的思考作者提供的实例属性和一些回调方法(touchStart, change, touchMove, pressMove, ...
- AlloyFinger.js 源码 学习笔记及原理说明
此手势库利用了手机端touchstart, touchmove, touchend, touchcancel原生事件模拟出了 rotate touchStart multipointStart ...
- lazy-load-img.js 源码 学习笔记及原理说明
lazy-load-img.js? 1. 什么鬼? 一个轻量级的图片懒加载,我个人很是喜欢. 2. 有什么优势? 1.原生js开发,不依赖任何框架或库 2.支持将各种宽高不一致的图片,自动剪切成默认图 ...
- observe.js 源码 学习笔记
/** * observejs --- By dnt http://kmdjs.github.io/ * Github: https://github.com/kmdjs/observejs * MI ...
- Vue.js 源码学习笔记 -- 分析前准备1 -- vue三大利器
主体 实例方法归类: 先看个作者推荐, 清晰易懂的 23232 简易编译器 重点: 最简单的订阅者模式 // Observer class Observer { constructor (d ...
- Vue.js 源码学习笔记 - 细节
1. this._eventsCount = { } 这是为了避免不必要的深度遍历: 在有广播事件到来时,如果当前 vm 的 _eventsCount 为 0, 则不必向其子 vm 继续传播该 ...
随机推荐
- Eclipse 发布网站到linux的tomcat
1. 安装Eclipse tomcat插件 2. 打包程序 需要把程序打成war包,右键工程,如下操作: 3. 上传到linux 3.1 上传到tomcat目录下 tomcat/webapps/XXX ...
- HTML URL
HTML 统一资源定位器(Uniform Resource Locators) URL 是一个网页地址. URL可以由字母组成,如"runoob.com",或互联网协议(IP)地址 ...
- 关于如何正确地在android项目中添加第三方jar包
在android项目中添加第三方jar包虽然不是一个很复杂的问题,但是确实给很多开发者带来了不小的困扰.我自己就曾经碰到过calss not found exception.error inflati ...
- Beanstalkd使用
Beanstalkd,一个高性能.轻量级的分布式内存队列系统,最初设计的目的是想通过后台异步执行耗时的任务来降低高容量Web应用系统的页面访问延迟,支持过有9.5 million用户的Facebook ...
- spring mvc redirect设置FlashAttribute
在Controller中设置: @RequestMapping("/redir") public String redir(Model model, RedirectAttribu ...
- Spring MVC URL的映射问题 ;Spring MVC 跳转与iframe包含地址问题
/login/login.html 进行form提交,登录之后的页面位于/main/frame.jsp; 这样的controller中的地址需要映射成/main/login.do,然后在control ...
- loadrunner:从数据库中取值进行参数化
下面我们介绍用数据库中的用户名来参数化登陆用户名. 框选住登陆名,点鼠标右键,弹出对话框,选择"替换为新参数"弹出对话框,此时参数名输入:name,参数类型选择File,如图 点& ...
- # C#不登录电脑启动程序
我们知道开机自启动程序如果在用户不登录的情况下是不启动的,但是服务类程序是可以跨过用户登录启动的,例如IIS服务,SQL服务.如果我们已经写好了桌面应用程序,又希望他开机自启动,那就需要借助系统服务在 ...
- boneCP的连接管理
boneCP连接的实现 boneCP自己实现了标准的java.sql.Connection接口,除了会持有Connection对象之外,还会拥有一些属性用于标记连接的创建时间,空闲时间等. 比较重要的 ...
- 时间处理之strtotime
strtotime (PHP 4, PHP 5, PHP 7)strtotime - 将任何英文文本的日期时间描述解析为 Unix 时间戳说明 int strtotime ( string $time ...