本文封装的简单例子如下:
/**
* 多属性动画
* @param {Element} element 要做动画的元素
* @param {Object} targetObj 属性目标值的对象 封装了所有要做动画的属性及其目标值
* @param {number} timeCost 动画耗时,单位毫秒
* @param {Function} callback 动画结束的回调函数
* animate(box,{left:"200px",top:"500px",opacity:0.5},3000,()=>console.log("动画结束"))*/
由于代码块中的解释已经很详细了,在此就不做过多的说明名了,看代码即可
代码:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="X-UA-Compatible" content="ie=edge" />
<title>Document</title>
<style>
* {
margin: 0;
padding: 0;
}
#box {
width: 100px;
height: 100px;
background-color: blueviolet;
position: absolute;
top: 0;
left: 0;
}
</style>
</head>
<body>
<div id="box"></div>
<script>
// 测试封装效果的
box.onclick = function (e) {
animate(box, { left: "200px", top: "500px", opacity: 0.5 }, 3000, function () { console.log("动画结束") })
}
</script>
<script>
/* 从样式属性值中截取单位 */
// getUnit("200.123px")
function getUnit(value) {
// 定义所有的数字
var digits = "0123456789."
// 将入参先转换为string(以便计算字符数)
value = value + ""//200px
/* 遍历入参的每一个字符 */
for (var i = 0; i < value.length; i++) {
// 提取出每一个字符
var char = value[i]
// 看看当前字符是否是【数字字符】
// 如果不是【数字字符】 则当前位i一直到末尾皆为单位
if (digits.includes(char) === false) {
// 从当前位i一直截取到末尾 得到单位 返回之
return value.slice(i)
}
}
return ""
}
// 封装一个名为animate的函数
function animate(element, targetObj, timeCost = 1000, callback = null) {
// 定义每帧时长为40毫秒
var FRAEME_COST = 40
// 计算总帧数(半帧按一帧算)3000/40
var frames = Math.ceil(timeCost / FRAEME_COST)
// 记录当前运行到了第几帧
var currentFrame = 0
// 提前拿到动画前所有CSS属性 {left:0,top:0,opacity:1...}
var exStyles = window.getComputedStyle(element)
/* 每个CSS属性每帧应该偏移多少 */
// speed者 每帧偏移量也
var speedObj = {}
/* 计算每帧应该移动多少 */
// key分别为 left top opacity
for (var key in targetObj) {
// 拿出left属性的目标值 200px
var targetValue = targetObj[key]
// (目标值 - 当前值)/ 总帧数
/*
// 动画速度对象
{
// left每帧动画速度
left:{
// 每帧偏移2
offset:2,
// 单位px
unit:"px"
},
top:{offset:5,unit:"px"},
opacity:{offset:0.005,unit:""}
}
*/
speedObj[key] = {
// left每帧的偏移量
offset: (parseFloat(targetValue) - parseFloat(exStyles[key])) / frames,
// 将单位也揪出来
unit: getUnit(targetValue)
}
}
/* 开始做动画 */
var timer = setInterval(
/* 每40毫秒执行一下这个破函数:在上一帧(frame)的基础上 所有目标样式都按动画速度speedObj[key]偏移一丢丢 */
function () {
// 在每帧中对每个属性按动画速度做偏移
for (var key in speedObj) {
/* 拿出left属性每帧的偏移量 */
var offset = speedObj[key].offset
// 拿出left属性的单位
var unit = speedObj[key].unit
// 将元素的left设置为一个新的值(偏移了一丢丢)
// element.style.left = (0+2)+"px"
element.style[key] =
// 拿出上一帧的left的值(锯掉单位)
parseFloat( getComputedStyle(element)[key] )
// 加上每帧应走的偏移量
+ offset
// 拼接单位
+ unit
}
/* 看看够帧数了没有 */
if (++currentFrame === frames) {
// 帧数一满 停止动画
clearInterval(timer)
// 暴力矫正误差(将所有属性都打到目标值)
// {left:"200px",top:"500px",opacity:0.5}
for (var key in targetObj) {
// box.style.left = "200px"
element.style[key] = targetObj[key]
}
}
},
// 每40毫秒一帧
FRAEME_COST
);
}
</script>
</body>
</html>