动画的基础知识
* 动画
* 1.CSS3动画(能用C3解决的动画绝对不用JS,因为C3动画性能好)
* + transition过渡动画
* + animation帧动画
* + transform是变形不是动画(经常依托某一种动画让元素在一定时间内实现变形效果)
*
* 2.JS动画
* + 定时器
* + requestAnimationFrame(JS中的帧动画)
* + 所谓的canvas动画就是JS基于定时器完成(canvas是一个HTML标签,可以理解为是一个画布,我们可以基于JS在画布上绘制出图像和效果)
*
* 3.FLASH动画(ActionScript)
//=>需求:让BOX盒子从最左边运动到最右边(结束) [修改BOX的LEFT值即可]
let minL = 0,
maxL = document.documentElement.clientWidth - box.offsetWidth;
//=>[固定步长的匀速运动]
let step = 5,
autoTimer = setInterval(() => {
let curL = box.offsetLeft;//=>我们用左偏移临时代替一下LEFT值
curL += step;
if (curL >= maxL) {
//=>固定步长的情况下做边界判断:都是先加上步长在做判断,验证我如果走这一步会不会超,如果超了,我们直接运动到末尾即可,没超才走一步
box.style.left = maxL + 'px';
clearInterval(autoTimer);
return;
}
box.style.left = curL + 'px';
}, 17);//=>17/13MS都是比较好的动画执行时间(浏览器不会出现卡顿)
//=>[固定时间的匀速运动]
let duration = 1000,//=>总时间
interval = 17,//=>频率:多长时间迈一步
begin = 0,//=>起始位置
target = maxL,//=>目标位置
change = target - begin,//=>总距离:目标值(TARGET)-起始值(BEGIN)
time = 0;//=>已经运动的时间
let autoTimer = setInterval(() => {
//=>根据公式计算出当前盒子应有的位置
time += interval;//=>time+=17;
if (time >= duration) {
//=>当前运动的时间超过总时间:到达边界
box.style.left = target + 'px';
clearInterval(autoTimer);
return;
}
let curL = time / duration * change + begin;
box.style.left = curL + 'px';
}, interval);
//1.第一种思路:步长=总距离/总时间*频率,剩下变为固定步长的匀速运动了
//2.在JS中基于定时器完成动画,不论是固定步长还是固定时间,只要算出当前盒子应该运动的位置即可(新的位置信息)
//t:TIME当前运动的时间
//d:DURATION总时间
//b:BEGIN起始位置
//c:CHANGE总距离 (TARGET-BEGIN)
// t/d:当前已经运动的时间/总时间 =>当前动画完成的百分比
// t/d*c:当前动画完成的百分比*总距离 =>当前已经走的距离
// t/d*c+b:当前走的距离+盒子的起始位置 =>当前盒子应该有的位置
规定时间内的多方向匀速运动
/*
* 规定时间内的多方向匀速运动
* TIME 当前运动时间
* DURATION 总时间
*
* [记录每一个方向的起始位置、目标值、总距离]
* BEGIN 起始位置
* TARGET 目标位置
* CHANGE 总距离
*/
let time = 0,
duration = 1000;
let target = {
left: document.documentElement.clientWidth - box.offsetWidth,
top: document.documentElement.clientHeight - box.offsetHeight,
width: 50,
height: 50,
fontSize: 30
};
// let change = {
// left: target['left'] - begin['left'],
// top: target['top'] - begin['top']
// };
//=>根据目标值计算出当前元素每一个运动方向的总距离(前提:计算出每个方向的起始值)
let change = {};
let begin = {};
for (let attr in target) {
if (target.hasOwnProperty(attr)) {
begin[attr] = parseFloat(window.getComputedStyle(box)[attr]);
change[attr] = target[attr] - begin[attr];
}
}
let animateTimer = setInterval(() => {
time += 17;
if (time >= duration) {
clearInterval(animateTimer);
for (let key in target) {
if (target.hasOwnProperty(key)) {
box.style[key] = target[key] + 'px';
}
}
return;
}
//=>根据目标值中的方向,基于公式计算出每一个方向的当前位置
let cur = {};
for (let attr in target) {
if (target.hasOwnProperty(attr)) {
cur[attr] = time / duration * change[attr] + begin[attr];
}
}
for (let key in cur) {
if (cur.hasOwnProperty(key)) {
box.style[key] = cur[key] + 'px';
}
}
}, 17);
封装基础版动画库
/*==ANIMATE动画库==*/
~function () {
//=>准备操作CSS样式的方法 GET-CSS/SET-CSS/SET-GROUP-CSS/CSS
let utils = (function () {
//=>获取样式
let getCss = (ele, attr) => {
let val = null,
reg = /^-?\d+(\.\d+)?(px|rem|em)?$/;
if ('getComputedStyle' in window) {
val = window.getComputedStyle(ele)[attr];
if (reg.test(val)) {
val = parseFloat(val);
}
}
return val;
};
//=>设置样式
let setCss = (ele, attr, value) => {
if (!isNaN(value)) {
if (!/^(opacity|zIndex)$/.test(attr)) {
value += 'px';
}
}
ele['style'][attr] = value;
};
//=>批量设置样式
let setGroupCss = (ele, options) => {
for (let attr in options) {
if (options.hasOwnProperty(attr)) {
setCss(ele, attr, options[attr]);
}
}
};
//=>合并为一个
let css = (...arg) => {
let len = arg.length,
fn = getCss;
if (len >= 3) {
fn = setCss;
}
if (len === 2 && typeof arg[1] === 'object') {
fn = setGroupCss;
}
return fn(...arg);
};
return {css}
})();
//=>EFFECT:准备运动的公式
let effect = {
Linear: (t, b, c, d) => t / d * c + b
};
//=>封装动画库
window.animate = function (ele, target = {}, duration = 1000, callback = new Function()) {
//=>不传递CALL-BACK,让其默认为一个空函数(回调函数:当动画结束后做什么事,都放到回调函数完成即可)
if (typeof duration === 'function') {
//=>我们有四个形参,但是传递的时候只传递三个,最后一个回调函数传递给duration这个参数了,我们需要改一下参数的值
callback = duration;
duration = 1000;
}
//1.基于TARGET计算出BEGIN/CHANGE
let begin = {},
change = {},
time = 0;
for (let attr in target) {
if (target.hasOwnProperty(attr)) {
begin[attr] = utils.css(ele, attr);
change[attr] = target[attr] - begin[attr];
}
}
//2.实现动画
clearInterval(ele.animteTimer);//=>在给当前元素设置新的动画之前,先清空原有正在运行的动画(防止多动画共存,把动画的返回值赋值给当前元素的自定义属性,这样只要元素不变,我们不管啥时候在哪执行都可以清除元素的动画)
ele.animteTimer = setInterval(() => {
time += 17;
//=>边界判断
if (time >= duration) {
utils.css(ele, target);
clearInterval(ele.animteTimer);
callback.call(ele);//=>动画完成后执行CALL-BACK(并且让回调函数中的THIS是当前操作的元素本身)
return;
}
//=>依托TARGET计算出每个方向的当前位置
let cur = {};
for (let attr in target) {
if (target.hasOwnProperty(attr)) {
cur[attr] = effect.Linear(time, begin[attr], change[attr], duration);
}
}
utils.css(ele, cur);
}, 17);
};
}();
animate(box, {
top: 0,
left: 0
}, function () {
//=>this:box
this.style.borderRadius = '50%';
this.style.backgroundColor = 'green';
});
JQ中的动画方法
let $box = $('#box');
//=>animate:$.prototype
//EFFECT:linear\ease\ease-in\ease-out\ease-in-out
//=>stop:结束正在运行的动画,继续执行下一个新的动画
//=>finish:同stop一样也是结束正在运行的动画(结束动画后让元素立即运动到目标位置,从上一个动画的目标位置作为下一个动画的起始位置,stop是从哪停止的,就从哪开始)
// $box.stop().animate({
// top: 300,
// left: 500
// }, 500, () => {
// $box.css({
// borderRadius: '50%',
// background: 'lightblue'
// });
// });
// $box.animate({
// top: 0,
// left: 0
// }, 5000);
// setTimeout(() => {
// $box.finish();
// //$box.finish();
// }, 1000);
//=>快捷动画
//1. show/hide/toggle
//2. fadeIn/fadeOut/fadeToggle
//3. slideDown/slideUp/slideToggle
// 可以指定具体运动时间,也可以指定'slow' / 'fast'