1. animate
// 匀速动画:
// 1. 获取动画运动的总时长 duration
// 2. 求终点坐标(目标值,如 opacity 的最终值是1)
// 3. 求起点坐标(起点初始值)
// 4. 求路程:终点坐标 - 起点坐标
// 5. 设置计时 curT,记录从开始运动经过的时间
// 6. 设置定时器,累加 curT,计算经过 curT 时间后元素所处的位置,并且把这个位置设置给元素;
// 7. 做过界判断
// 封装一个动画库:匀速动画
// 效果
const { css } = window.utils;
const Effects = {
// linear: function (...) {....}, 箭头函数还需乖乖的写
getName: (a, b) => {console.log(a, b)}, // 箭头函数要写全
linear (curT, begin, change, duration = 2000) {
return (change / duration) * curT + begin;
} // 对象的简洁语法,linear 是 Effects 对象的一个属性,liner 不是变量
};
function animate({ ele, target = {}, duration = 2000, after }) {
// 从实参解构 ele, target, duration, after
// 1. 做参数合法性校验,如果不合法抛出异常
if (typeof ele !== 'object') {
throw TypeError('ele is not a DOM-Element');
}
// 2. 求动画需要的参数: target 和 duration 以及通过参数的形式传进来了,不用单独求
// 2.1 求起始位置和路程
let begin = {}; // 设置空对象用来装 target 里面对应的属性的初始值
let change = {}; // 因为 target 不止一个属性,每一个属性都有一个路程,所以我们需要一个对象遍历 target 把 target 中的属性取出来
for (let attr in target) {
if (target.hasOwnProperty(attr)) {
begin[attr] = css(ele, attr); // 把 target 中属性的初始值计算出来,并且保存到begin对象中
change[attr] = target[attr] - begin[attr]; // 根据终点和起点的值计算该属性路程
}
}
// // 2.2 求路程:终点 - 起点
// let change = {}; // 因为target不止一个属性,每一个属性都有一个路程,所以我们需要一个对象
// for (let key in target) {
// if (target.hasOwnProperty(key)) {
// change[key] = target[key] - begin[key];
// }
// }
// 2.3 设置计时器变量
let curT = 0;
// 3. 利用定时器启动动画:通过元素对象自定义属性记录定时器id
if (ele.timerID) clearInterval(ele.timerID); // 开启下一次动画之前把前面的动画清除掉,防止出现动画累加
ele.timerID = setInterval(() => {
// 3.1 累加时间
curT += 10;
// 3.2 过界判断
if (curT >= duration) {
clearInterval(ele.timerID); // 清定时器停止动画
css(ele, target); // 批量设置元素到终点
/*if (typeof after === 'function') {
after.call(ele); // 把钩子函数中的this处理成元素对象
}*/
typeof after === 'function' && after.call(ele); // 等效于上面的if语句
return;
}
// 3.3 求经过 curT 时间后元素各个属性所处的位置
let curState = {}; // 因为 target 中有多个属性,所以需要把所有的属性经过 curT 时间后的走过的路程计算出来
for (let prop in target) {
if (target.hasOwnProperty(prop)) {
curState[prop] = Effects.linear(curT, begin[prop], change[prop], duration);
}
}
// 3.4 把上一步求出来的位置设置给元素
css(ele, curState);
}, 10)
}
let box = document.getElementById('box');
let box2 = document.getElementById('box2');
animate({
ele: box, // 元素对象
target: { // 多方向终点位置坐标集合
left: 850,
top: 400
},
duration: 2000, // 过渡时间
after: function () { // 动画执行结束后执行的函数(现在不执行,未来某个时刻会执行的函数叫做 钩子
【 hook 】)
console.log('终于执行完了');
this.style.color = 'red'; // 一般把钩子中 this 处理成元素对象
}
});
animate({
ele: box2, // 元素对象
target: { // 多方向终点位置坐标集合
left: 350,
top: 200
},
duration: 2000, // 过渡时间
after: function () { // 动画执行结束后执行的函数(现在不执行,未来某个时刻会执行的函数叫做 钩子
【 hook 】)
console.log('终于执行完了');
}
});
2. animate最终版
~function () {
const { css } = window._utils
// Effect 运动方式
const Effect = {
// Linear: function() {}
/**
* 匀速运动公式
* @param t 当前时间
* @param b 起始值
* @param c 改变值(目标值 - 起始值)
* @param d 过渡时间
*/
linear(t, b, c, d) {
return t * (c / d) + b
},
easeIn(t, b, c, d) {
return c - Effect.easeOut(d - t, 0, c, d) + b
},
easeOut(t, b, c, d) {
if ((t /= d) < 1 / 2.75) {
return c * (7.5625 * t * t) + b
} else if (t < 2 / 2.75) {
return c * (7.5625 * (t -= 1.5 / 2.75) * t + 0.75) + b
} else if (t < 2.5 / 2.75) {
return c * (7.5625 * (t -= 2.25 / 2.75) * t + 0.9375) + b
} else {
return c * (7.5625 * (t -= 2.625 / 2.75) * t + 0.984375) + b
}
},
easeInOut(t, b, c, d) {
if (t < d / 2) {
return Effect.easeIn(t * 2, 0, c, d) * 0.5 + b
}
return Effect.easeOut(t * 2 - d, 0, c, d) * 0.5 + c * 0.5 + b
}
}
/**
*
* @param [object] ele 当前动画元素
* @param [object] target 目标值对象
* @param [number] duration 过渡时间
* @param [function] after 动画结束钩子函数
*/
function animate({ ele, target = {}, duration = 2000, after, before, run, effect = 'linear' }) {
// 参数判断处理
if (!ele || ele.nodeType !== 1) {
return console.error('缺少元素对象ele~')
}
// 动画开始之前的 钩子函数
;(typeof before === 'function') && before.call(ele)
// 收集参数 begin change
let begin = {}
let change = {}
for (let k in target) {
if (target.hasOwnProperty(k)) {
begin[k] = css(ele, k)
change[k] = target[k] - begin[k]
}
}
// 记录当前时间
let time = 0
// 定时器间隔时间
const interval = 10
// 动画累积
ele.timer && clearInterval(ele.timer)
// 创建动画定时器
ele.timer = setInterval(() => {
// 动画执行过程中的 钩子函数
;(typeof run === 'function') && run.call(ele)
// 记录当前动画时间
time += interval
if (time >= duration) {
// 修正元素动画目标值
css(ele, target)
clearInterval(ele.timer)
// 动画结束后的 钩子函数
;(typeof after === 'function') && after.call(ele)
return
}
// 根据当前时间 计算当前状态
let curState = {}
for (let k in target) {
if (target.hasOwnProperty(k)) {
curState[k] = Effect[effect](time, begin[k], change[k], duration)
}
}
// 将当前状态 设置给当前元素
css(ele, curState)
}, interval)
}
// 过载到全局
window.$animate = animate
}()
3. 动画运动公式
var animationEffect = {
Linear: function (t, b, c, d) {
return c * t / d + b;
},
Bounce: {
easeIn: function (t, b, c, d) {
return c - animationEffect.Bounce.easeOut(d - t, 0, c, d) + b;
},
easeOut: function (t, b, c, d) {
if ((t /= d) < (1 / 2.75)) {
return c * (7.5625 * t * t) + b;
} else if (t < (2 / 2.75)) {
return c * (7.5625 * (t -= (1.5 / 2.75)) * t + .75) + b;
} else if (t < (2.5 / 2.75)) {
return c * (7.5625 * (t -= (2.25 / 2.75)) * t + .9375) + b;
} else {
return c * (7.5625 * (t -= (2.625 / 2.75)) * t + .984375) + b;
}
},
easeInOut: function (t, b, c, d) {
if (t < d / 2) {
return animationEffect.Bounce.easeIn(t * 2, 0, c, d) * .5 + b;
}
return animationEffect.Bounce.easeOut(t * 2 - d, 0, c, d) * .5 + c * .5 + b;
}
},
Quad: {
easeIn: function (t, b, c, d) {
return c * (t /= d) * t + b;
},
easeOut: function (t, b, c, d) {
return -c * (t /= d) * (t - 2) + b;
},
easeInOut: function (t, b, c, d) {
if ((t /= d / 2) < 1) {
return c / 2 * t * t + b;
}
return -c / 2 * ((--t) * (t - 2) - 1) + b;
}
},
Cubic: {
easeIn: function (t, b, c, d) {
return c * (t /= d) * t * t + b;
},
easeOut: function (t, b, c, d) {
return c * ((t = t / d - 1) * t * t + 1) + b;
},
easeInOut: function (t, b, c, d) {
if ((t /= d / 2) < 1) {
return c / 2 * t * t * t + b;
}
return c / 2 * ((t -= 2) * t * t + 2) + b;
}
},
Quart: {
easeIn: function (t, b, c, d) {
return c * (t /= d) * t * t * t + b;
},
easeOut: function (t, b, c, d) {
return -c * ((t = t / d - 1) * t * t * t - 1) + b;
},
easeInOut: function (t, b, c, d) {
if ((t /= d / 2) < 1) {
return c / 2 * t * t * t * t + b;
}
return -c / 2 * ((t -= 2) * t * t * t - 2) + b;
}
},
Quint: {
easeIn: function (t, b, c, d) {
return c * (t /= d) * t * t * t * t + b;
},
easeOut: function (t, b, c, d) {
return c * ((t = t / d - 1) * t * t * t * t + 1) + b;
},
easeInOut: function (t, b, c, d) {
if ((t /= d / 2) < 1) {
return c / 2 * t * t * t * t * t + b;
}
return c / 2 * ((t -= 2) * t * t * t * t + 2) + b;
}
},
Sine: {
easeIn: function (t, b, c, d) {
return -c * Math.cos(t / d * (Math.PI / 2)) + c + b;
},
easeOut: function (t, b, c, d) {
return c * Math.sin(t / d * (Math.PI / 2)) + b;
},
easeInOut: function (t, b, c, d) {
return -c / 2 * (Math.cos(Math.PI * t / d) - 1) + b;
}
},
Expo: {
easeIn: function (t, b, c, d) {
return (t == 0) ? b : c * Math.pow(2, 10 * (t / d - 1)) + b;
},
easeOut: function (t, b, c, d) {
return (t == d) ? b + c : c * (-Math.pow(2, -10 * t / d) + 1) + b;
},
easeInOut: function (t, b, c, d) {
if (t == 0) return b;
if (t == d) return b + c;
if ((t /= d / 2) < 1) return c / 2 * Math.pow(2, 10 * (t - 1)) + b;
return c / 2 * (-Math.pow(2, -10 * --t) + 2) + b;
}
},
Circ: {
easeIn: function (t, b, c, d) {
return -c * (Math.sqrt(1 - (t /= d) * t) - 1) + b;
},
easeOut: function (t, b, c, d) {
return c * Math.sqrt(1 - (t = t / d - 1) * t) + b;
},
easeInOut: function (t, b, c, d) {
if ((t /= d / 2) < 1) {
return -c / 2 * (Math.sqrt(1 - t * t) - 1) + b;
}
return c / 2 * (Math.sqrt(1 - (t -= 2) * t) + 1) + b;
}
},
Back: {
easeIn: function (t, b, c, d, s) {
if (s == undefined) s = 1.70158;
return c * (t /= d) * t * ((s + 1) * t - s) + b;
},
easeOut: function (t, b, c, d, s) {
if (s == undefined) s = 1.70158;
return c * ((t = t / d - 1) * t * ((s + 1) * t + s) + 1) + b;
},
easeInOut: function (t, b, c, d, s) {
if (s == undefined) s = 1.70158;
if ((t /= d / 2) < 1) {
return c / 2 * (t * t * (((s *= (1.525)) + 1) * t - s)) + b;
}
return c / 2 * ((t -= 2) * t * (((s *= (1.525)) + 1) * t + s) + 2) + b;
}
},
Elastic: {
easeIn: function (t, b, c, d, a, p) {
if (t == 0) return b;
if ((t /= d) == 1) return b + c;
if (!p) p = d * .3;
var s;
!a || a < Math.abs(c) ? (a = c, s = p / 4) : s = p / (2 * Math.PI) * Math.asin(c / a);
return -(a * Math.pow(2, 10 * (t -= 1)) * Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
},
easeOut: function (t, b, c, d, a, p) {
if (t == 0) return b;
if ((t /= d) == 1) return b + c;
if (!p) p = d * .3;
var s;
!a || a < Math.abs(c) ? (a = c, s = p / 4) : s = p / (2 * Math.PI) * Math.asin(c / a);
return (a * Math.pow(2, -10 * t) * Math.sin((t * d - s) * (2 * Math.PI) / p) + c + b);
},
easeInOut: function (t, b, c, d, a, p) {
if (t == 0) return b;
if ((t /= d / 2) == 2) return b + c;
if (!p) p = d * (.3 * 1.5);
var s;
!a || a < Math.abs(c) ? (a = c, s = p / 4) : s = p / (2 * Math.PI) * Math.asin(c / a);
if (t < 1) return -.5 * (a * Math.pow(2, 10 * (t -= 1)) * Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
return a * Math.pow(2, -10 * (t -= 1)) * Math.sin((t * d - s) * (2 * Math.PI) / p) * .5 + c + b;
}
}
};