通过自定义动画,可以很好提升对源生JavaScript知识的理解,主要涉及的技术:
1、setInterval 最小支持10毫秒,低于10毫秒按10秒计算,对于Chrome最少可以支持到4ms。
2、setInterval处理函数作用域,使用let锁定处理函数变量的作用
3、动画原理,在明确动画持续时间下,划分多个时间片段,改变元素的状态,达到目标值为止,结束动画
4、需要有面向对象思维,处理多个属性的动画,使用{}构建多个动画的信息
完整代码
/**
* 自定义元素的动画,支持多个属性
*
* @param {Object} el 动画元素
* @param {Object}} option 动画参数 {left:50,top:500} 注意:不要带单位 错误{left:50px} 正确{left:50}
* @param {Number} speed 动画持续时间(秒)
*/
function animate(el, option, speed = 0.5) {
if (!el || !option) {
return;
}
function run() {
animateData = {};//动画数据
var needTime = speed * 1000;
var cs = window.getComputedStyle(el);
//计算方向、前进距离
//这里使用let的原因是锁定key的作用域,否则setInterval执行函数的key值总是最后一次循环的key,就不能实现多个属性的动画效果
for (let key in option) {
var currValue;//当前值
animateData[key] = {};
//样式属性还是普通属性
animateData[key].whereProperty = null;
if ((currValue = cs[key])) {
animateData[key].whereProperty = "style";//自定义属性
} else if ((currValue = el[key])) {
animateData[key].whereProperty = "attr";//普通属性
}
if (!currValue) {
continue;
}
//目标值
var targetValue = parseFloat(option[key]);
//是否带px
animateData[key].isPx = false;//是否带px
if ((currValue + "").endsWith("px")) {//带px
animateData[key].isPx = true;
}
//方向
animateData[key].direction = "-";
if (targetValue > parseFloat(currValue)) {
animateData[key].direction = "+";
}
//当前值(不带单位)
animateData[key].value = parseFloat(currValue);
//每次移动的距离(需要计算)
animateData[key].step = (targetValue - animateData[key].value) / (needTime / 10);
//开始移动
var count = 0;
animateData[key].interval = setInterval(function () {
//需通过key重新获取,不能直接使用targetValue,否则在serInterval中使用的是最后的targetValue
var targetValue = parseFloat(option[key]);
animateData[key].value += animateData[key].step;//正方向或负方向
//达到目标
if ((animateData[key].direction == "+" && animateData[key].value >= targetValue)
|| (animateData[key].direction == "-" && animateData[key].value <= targetValue)
) {
animateData[key].value = targetValue;//防止超过目标
}
// start 移动操作
if (animateData[key].whereProperty == "style") {
el.style[key] = animateData[key].value + (animateData[key].isPx ? "px" : 0);
} else {
el[key] = animateData[key].value + (animateData[key].isPx ? "px" : 0);
}
// end 移动操作
//移动结束,停止计时器
if (animateData[key].value == targetValue) {
clearInterval(animateData[key].interval);
return;
}
}, 10);
}
}
run();
}