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; } }};