动画的原理
匀速运动
// 1. 匀速运动 s = vt v不变
let btn = document.querySelector("button");
let div = document.querySelector(".box");
btn.onclick = function () {
starMove(div, 300)
};
// 运动的元素 运动到的位置 向后+ 还是向前-
function starMove(dom, target) {
clearInterval(dom.timer);
let speed = 7; // 匀速的关键 每次运动的距离一样
dom.timer = setInterval(() => {
// 控制左右的方向
speed = (target - dom.offsetLeft) > 0 ? 7 : -7;
// 判断停止的位置
// 目标位置 - 当前元素左侧的距离 < 指定速度 停止
if (Math.abs(target - div.offsetLeft) < Math.abs(speed)) {
clearInterval(dom.timer);
dom.style.left = target + "px";
} else {
dom.style.left = dom.offsetLeft + speed + "px";
}
}, 30);
}
缓动运动
为了让每个元素拥有自己独立的定时器,把定时器名称挂在元素的属性上。
缓动运动:先加速后减速做变换。
// 2. 缓冲运动 先加速 后减速 增加的步数越来越小
let btn = document.querySelector("button");
let div = document.querySelector(".box");
btn.onclick = function () {
startMove(div, 500)
};
function startMove(dom, target) {
let speed = null;
let step = 7;
clearInterval(dom.timer);
dom.timer = setInterval(() => {
// 左右运动 控制数值大小 缓动趋势
// 元素距离目标点 距离越小 运动速度(每次运动的距离)越小
speed = (target - dom.offsetLeft) / step;
// 取个整数 负数时 向下取整 整数时 向上取整
speed = speed > 0 ? Math.ceil(speed) : Math.floor(speed);
// 当元素左侧距离 达到目标点时 清除定时器
if (dom.offsetLeft == target) {
clearInterval(dom.timer);
} else {
// 具有左右两个方向的功能 speed为+ 加距离 为 - 减距离
dom.style.left = dom.offsetLeft + speed + "px";
}
}, 30);
};
属性的缓动运行
不同元素不同属性变化。
透明度的缓动:
// 获取对象特定属性值 返回类型是字符串
function getStyle(dom, attr) {
if (window.getComputedStyle) {
return window.getComputedStyle(dom, null)[attr];
} else {
return dom.currentStyle[attr];
}
}
// 3.缓动透明度
let btn = document.querySelector("button");
let div = document.querySelector(".box");
btn.onclick = function () {
// 透明度扩大100倍
startMove(div, "opacity", 20);
};
// 透明度缓冲
function startMove(dom, target) {
clearInterval(dom.timer);
let speed = null,
iCur = null;
let step = 7;
dom.timer = setInterval(function () {
iCur = parseFloat(getStyle(dom, "opacity")) * 100;
// speed为 缓动趋势 同时具有左右的功能。
// 元素距离目标点 距离越小 运动速度(每次运动的距离)越小
speed = (target - iCur) / step;
// 取个整数 负数时 向下取整 整数时 向上取整
speed = speed > 0 ? Math.ceil(speed) : Math.floor(speed);
// 透明度到了
if (iCur == target) {
clearInterval(dom.timer);
} else {
dom.style.opacity = (iCur + speed) / 100;
}
}, 30);
};
封装适合所有属性的缓动:
// 4. 封装一个不同属性缓动
function startMove(dom, attr, target) {
let speed = null,
iCur = null;
let step = 7;
clearInterval(dom.timer);
dom.timer = setInterval(function () {
// 判断传入的属性是否 为 opacity 需要特殊处理
if (attr == "opacity") {
iCur = parseFloat(getStyle(dom, attr)) * 100;
} else {
iCur = parseFloat(getStyle(dom, attr));
}
// 确定属性类型 继续向下运行
speed = (target - iCur) / step; // 元素距离目标点距离越小速度越小
// 取个整数 负数时 向下取整 整数时 向上取整
speed = speed > 0 ? Math.ceil(speed) : Math.floor(speed);
if (iCur == target) {
clearInterval(dom.timer);
} else {
if (attr == "opacity") {
dom.style.opacity = (iCur + speed) / 100;
} else {
// 具有左右两个方向的功能 speed为+ 加距离 为 - 减距离
dom.style[attr] = iCur + speed + "px";
}
}
}, 30);
}
多个属性同时传入
不同元素多个属性发生改变。
回调机制:回调函数,动画完成后执行。
// 5. 对象形式传入属性
let btn = document.querySelector("button");
let div = document.querySelector(".box");
btn.onclick = function () {
// 透明度扩大100倍
startMove(div, {
width: 400,
height: 400,
opacity: 50 // 串值时也需要扩大100
});
};
// 属性对象:
// 1.遍历此对象属性,
// 2.判断属性中是否有透明度,有 值*100
// 3.改变值时:判断属性是否含有opacity 有 /100
// 4.设置标志 默认定时器可以清除 flag = true 循环外部设置
// 5.判断是否有属性没有变换完毕 变换对象.属性值 是否等于 属性集合.属性值 不等于 flag = false
// 6.属性遍历完毕,只要标志为真就说明所有属性完成变换 则清除定时器
// 7.回调函数,动画做完调用,在清除定时器之后
function startMove(dom, attrObj, callback) {
clearInterval(dom.timer);
let speed = null,
iCur = null;
const step = 7;
dom.timer = setInterval(function () {
let flag = true; // 标志
// 遍历属性集合对象
for (let attr in attrObj) {
// 判断属性 是否含有 opacity 取值
if (attr == "opacity") {
iCur = parseFloat(getStyle(dom, attr)) * 100;
} else {
iCur = parseFloat(getStyle(dom, attr));
}
// 缓动趋势
speed = (attrObj[attr] - iCur) / step;; // 元素距离目标点距离越小速度越小
// console.log(speed);
speed = speed > 0 ? Math.ceil(speed) : Math.floor(speed); // 取个整数
// 判断属性 是否含有 opacity 赋值
if (attr == "opacity") {
dom.style.opacity = (iCur + speed) / 100;
} else {
dom.style[attr] = iCur + speed + "px";
}
// 说明有属性没有变换完毕 标志取反
if (iCur != attrObj[attr]) {
flag = false;
}
}
// 属性对象遍历完毕 标志符为true则说明所有属性变换完毕 清除定时器
if (flag) {
clearInterval(dom.timer);
// 逻辑短路应用:callback不存在返回false不影响,callback存在之间返回callback()
callback && callback();
}
}, 30);
}