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 继续传播该 ...
随机推荐
- XML学习总结(二)——XML入门
XML学习总结(二)——XML入门 一.XML语法学习 学习XML语法的目的就是编写XML 一个XML文件分为如下几部分内容: 文档声明 元素 属性 注释 CDATA区 .特殊字符 处理指令(proc ...
- MyBatis 一对一关联查询
xml文件: <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE mapper PUBLIC & ...
- 编写Node.js原生扩展
Node.js是一个强大的平台,理想状态下一切都都可以用javascript写成.然而,你可能还会用到许多遗留的库和系统,这样的话使用c++编写Node.JS扩展会是一个不错的注意. 以下所有例子的源 ...
- There is no getter for property named 'userId' in 'class java.lang.String'
[ERROR] 2017-01-18 04:37:06:231 cn.dataenergy.common.CenterHandlerExceptionResolver (CenterHandlerEx ...
- PrefixSpan算法原理总结
前面我们讲到频繁项集挖掘的关联算法Apriori和FP Tree.这两个算法都是挖掘频繁项集的.而今天我们要介绍的PrefixSpan算法也是关联算法,但是它是挖掘频繁序列模式的,因此要解决的问题目标 ...
- python关于列表的操作
列表是Python中最基本的数据结构,列表是最常用的Python数据类型,列表的数据项不需要具有相同的类型.列表中的每个元素都分配一个数字 - 它的位置,或索引,第一个索引是0,第二个索引是1,依此类 ...
- iOS 图片水印、图片合成文字或图片实现
这个需求可能有时候会碰到,比如自己的照片加版权,打水印等 网上的方法,有不少感觉不全对,或者需求不是特全,这里我总结了3种场景下的需求: 1.本地图片合成文字 2.本地图片合成图片 3.网络图片先下载 ...
- ubuntu 压缩软件
7-zip 安装: sudo apt-get install p7zip-full 使用方法: 7z x file file是你要解压的文件名 更多使用方法: man 7z rar sudo apt- ...
- css 样式重置
html, body, div, span, applet, object, iframe, h1, h2, h3, h4, h5, h6, p, blockquote, pre, a, abbr, ...
- echarts 显示下载按钮,echarts 自定义按钮,echarts 添加按钮
echarts 显示下载按钮,echarts 自定义按钮,echarts 添加按钮 >>>>>>>>>>>>>>&g ...