高质量前端博主,点个关注不迷路🌸🌸🌸!
写在最前:
这篇文章是来填坑的,填上一次给大家讲轮播图的一个坑:关于封装动画函数的详细介绍,以及匀速和减速动画各自的封装原理,废话不多说,大家请阅读文章!
I. 总述
首先我们要明白封装动画函数是要干什么?
考虑这样一件事,例如我想要一个div元素,从一个位置,通过运动到达另一个位置。并且是左右移动,应该怎么做?
这个时候大家第一个想到的应该是用css的动画样式,动画样式固然可以实现,但是我们今天采用另一种思路:用JavaScript实现这个移动的效果,那么它实现动画效果的原理是什么呢?我们仍然以左右移动为例:
通过提前判定目标位置的坐标,而后计算出距离目标坐标需要给div设置的left值,最后,通过函数setInterval()函数连续的执行移动(间隔时间非常短,通常设置30ms,越短越好),但是每一次移动,仅仅移动一小段,由于间隔的时间很短,于是我们人眼的错觉会认为是在缓缓移动。
这就是实现的原理了,而常用的两类动画函数封装有匀速运动的封装和减速运动的封装,下面我们就一一给予原理的讲解与编码。
II. 匀速动画的封装原理与编码
首先,我们进行匀速运动动画函数的封装:
我们一步一步来,首先,我们新建一个js文件,名字比如起一个animate.js,并把函数的框架写出来:
function animate(obj, target, callback) {
}
从框架中,我们看到有三个参数:obj、target 和 callback,我们逐一解释它们的作用:
1️⃣ obj:obj是需要执行动画的元素,例如我们前面举的例子,那么div就是我们的obj,传入div对应的dom对象即可。(jquery对象也可以)
2️⃣ target:这是目标位置的left值,当然了如果后面大家需要向别的方向做动画,例如上下方向,那这个target就是目标的top值或者bottom值。
3️⃣ callback:这是完成了本次动画后执行的回调函数,我们要理解"完成"二字:完成指的是已经完成了从一个地方到目的地,而不是我刚才说的setInterval()函数的每一次执行,简单的说,这里的完成就是指的是已经到达目的地了,然后会执行的函数。
于是,接下来就像我刚才提到的那样,我们先在里面写一个setInterval()函数,并设置一个很短的调用间隔时间,比如30ms调用一次:
function animate(obj, target, callback) {
obj.timer = setInterval(function () {
},30);
}
这里obj.timer指的就是obj的setInterval()函数,我们知道每一个dom元素或者jQuery元素对象都有一个timer属性,这个timer属性就是指它绑定的定时器,这个定时器其实就是setInterval(),通俗的讲就是这一步就给obj加了一个定时器timer,加的方式是setInterval()函数。
那看到这里,大家应该会有一个想法:那就是会不会出现多个定时器的混叠,比如每次调用了这个函数,传入一个obj,这个obj就会被安装一个定时器,这样就会引起混叠和混乱,于是我们需要在安装定时器前加一句代码:
function animate(obj, target, callback) {
clearInterval(obj);
obj.timer = setInterval(function () {
},30);
}
没错,就是用经典的clearInterval()(不了解的朋友可以回看我之前关于setInterval()函数的博客)。
这之后,我们开始分析div的移动过程,请看下图:
首先,我们拿到了div这个元素的dom对象obj后,我们理所应当的可以拿到它当前的left样式值,拿到样式值的代码大家都会写,应该是这样的:
var current_left = obj.css("left")
之后,我们每一次调用setInterval()要走的距离,由于时间只有30ms,因此不能走的太远,否则就没有动画的感觉了,而是有很突兀的瞬移感,因此我推荐每次移动5像素,但由于传入的目标值可以大于当前的left值,也可以小于当前的left值,因此我们加一个判断:
var step;
if(parseInt(current_left) > target){
step = -5;
}
else{
step = 5
}
有了每一次移动的距离step(也叫步长,后面我们都叫它步长),我们也有目标left值target,于是很自然的,我们可以写出这样的逻辑判断进行匀速移动,于是完整的匀速代码也就完成了:
function animate(obj, target, callback) {
clearInterval(obj);
obj.timer = setInterval(function () {
var current_left = obj.css("left");
var step;
if(parseInt(current_left) > target){
step = -5;
}
else{
step = 5
}
if (parseInt(current_left) == target) {
clearInterval(obj.timer);
if (callback) {
callback();
}
}
obj.css("left", parseInt(current_left) + step + "px");
}, 30)
}
可以看到,当完成了运动,也即满足:
parseInt(current_left) == target
则会**先把定时器解除,防止再运动;与此同时,执行回调函数callback()**。
那说到这里,匀速运动的封装我们就完成了,为了使讲解更加生动有趣,我们用一个小例子测试一下动画函数好不好用:
<!DOCTYPE html>
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>animation</title>
<style>
#father {
position: relative;
height: 200px;
}
#son {
position: absolute;
width: 200px;
height: 200px;
border: 1px solid black;
background-color: aqua;
}
#btn {
width: 120px;
height: 30px;
line-height: 30px;
margin: 0 auto;
margin-top: 100px;
border: 1px solid black;
text-align: center;
}
</style>
</head>
<body>
<div id="father">
<div id="son"></div>
</div>
<div id="btn">点击运动</div>
<script type="text/javascript" src="../匀速动画/animate.js"></script>
<script type="text/javascript" src="../匀速动画/jquery.min.js"></script>
<script type="text/javascript">
var btn = $("#btn");
var div_obj = $("#son");
btn.bind("click", function () {
animate(div_obj, 400, function () {
alert("完成运动!")
})
})
</script>
</body>
</html>
其中,我们用这句话引入写好的动画函数的js文件:
<script type="text/javascript" src="../匀速动画/animate.js"></script>
下面我还引入了jQuery,目的是简化代码,大家也可以用原生的DOM,都可以。
运行后,效果如下图所示:
III. 减速动画的封装原理与编码
最后,我们谈一谈减速动画要怎么封装,原理是什么。
所谓的减速,其实和我们中学物理学的减速运动很类似,也就是在运动的过程中,速度越走越慢,那么我们看一个式子:
v = s / t
这个公式不知道的,可以面壁思过了哈哈,中学物理的速度定义式子,从这个式子中,我们知道想要让速度越走越慢,有两个可行角度:
增加 s 或者减小 t
但是事实上,减小t并不现实,原因其实刚才已经说过了,30ms的每次执行移动时间间隔已经是一个极限的时间了,小于这个时间的移动,会变得很假,就不像是动画了,更像瞬移。于是结论就有了:
要实现减速运动 = = = 减小每一次移动的距离 s
那么我们要怎么减小每一次移动的距离s呢,一个很简单的想法就是每一次让移动的步长减小一个固定的常数,但如果每一次减小的都是一个固定的常数,那等于没有实现减速,而是速度变小的匀速,于是我们应该是每一次的s都能比前一次的小,于是我们可以用下面的这个式子:
var step = (target - parseInt(obj.css("left"))) / 10;
其中,step是当次移动的步长,target仍然是目标的left值,parseInt(obj.css)是当前的left值,那么每一次的step有没有减小呢?我们来计算一下:
例如当前的left值是100,目标是0,那么我们的步长应该是这样的:
第一次:step = (100 - 0) / 10 = 10 px
第二次:step = (100 - 10) / 10 = 9 px
第三次:step = (100 - 19) / 10 = 8.1 px
...
好了,于是计算了一下发现的确能使得步长step不断减小,也即我们上面式子中的s不断减小,因此这个式子貌似可行。
但是善于思考的朋友可能要质疑我了:如果按照这样的计算方式,那么理论上应该是永远也到不了目的地,因为每一次总会与目的地有一个细微的差值,其实这样的理解是对的,于是我们需要加这样一行代码:
step = step > 0 ? Math.ceil(step) : Math.floor(step);
这句代码**涉及了两个函数Math.ceil()和Math.floor()**,避免大家之前没见过,我们做一个简述:
Math.ceil()方法执行的是向上取整计算,它返回的是大于或等于函数参数,并且与之最接近的整数。
Math.floor() 方法执行的是向下取整计算,它返回的是小于或等于函数参数,并且与之最接近的整数。
于是加了这句话,会出现什么神奇的事情呢?我们看一下:
当前的left是91,目标是100,那么有:
step = (100 - 99) / 10 = 0.1
按理说,它应该走0.1,然后再留下0.9,但有了这个函数,它直接就走了1,也就是走到了目的地。这是对ceil()函数的解释,那对于floor()则逻辑是完全一样的,不做赘述了。
于是我们拿出刚才封装好的匀速函数,把step的公式换成上面的公式,就得到了减速运动的封装函数:
function animate(obj, target, callback) {
clearInterval(obj);
obj.timer = setInterval(function () {
var current_left = parseInt(obj.css("left"));
var step;
step = (target - current_left) / 10;
step = step > 0 ? Math.ceil(step) : Math.floor(step);
if (current_left == target) {
clearInterval(obj.timer);
if (callback) {
callback();
}
}
obj.css("left", current_left + step + "px");
}, 30)
}
继续用前面那个小例子,我们测试一下减速函数好不好用:
<!DOCTYPE html>
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>animation</title>
<style>
#father1,
#father2 {
position: relative;
height: 150px;
margin-top: 50px;
}
#son1,
#son2 {
text-align: center;
font-size: 30px;
line-height: 150px;
position: absolute;
width: 150px;
height: 150px;
border: 1px solid black;
background-color: aqua;
}
#btn {
width: 120px;
height: 30px;
line-height: 30px;
margin: 0 auto;
margin-top: 100px;
border: 1px solid black;
text-align: center;
}
</style>
</head>
<body>
<div id="father1">
<div id="son1">匀速</div>
</div>
<div id="father2">
<div id="son2">减速</div>
</div>
<div id="btn">点击运动</div>
<script type="text/javascript" src="../减速动画/animate2.js"></script>
<script type="text/javascript" src="../减速动画/animate.js"></script>
<script type="text/javascript" src="../减速动画/jquery.min.js"></script>
<script type="text/javascript">
var btn = $("#btn");
var div_obj1 = $("#son1");
var div_obj2 = $("#son2");
btn.bind("click", function () {
animate(div_obj1, 400, function () {
})
animate2(div_obj2, 400, function () {
})
})
</script>
</body>
</html>
为了方便大家对比,我这次把两个动画函数都引了进来,并且把匀速动画函数命名为animate,把减速动画函数命名为animate2,并复制了一个二号盒子,下面是效果展示:
好了,那到这里减速动画的封装原理和编码也讲解完毕,感谢大家的阅读!(评论区制定有本次讲解的两个动画函数的资源,大家可以自行下载js资源文件)
以上是本期的全部内容,喜欢的小伙伴们可以三连支持一下!💓💓💓!
另外本期博客参与了【新星计划】,还请大家支持一下🌟🌟🌟!
版权归原作者 跳探戈的小龙虾 所有, 如有侵权,请联系我们删除。