JavaScript动画函数封装详解:从原理到缓动与回调
本文详细解析JavaScript动画函数的封装过程。从基础的setInterval定时器原理出发,讲解如何移动元素位置。针对多元素动画内存泄漏问题,介绍通过对象属性独立管理定时器。进一步深入缓动算法,解决步长取整导致的定位偏差,实现精确停止。最后引入回调函数机制,完善动画完成后的逻辑处理,提供完整可用的JS动画封装方案。
一、动画函数原理
核心原理:通过定时器setInterval() 持续移动盒子位置。
实现步骤:
- 获取盒子当前位置
- 使盒子在当前位置增加1个移动距离
- 利用定时器不断重复此操作
- 增加结束定时器的条件
- 注意此元素需添加定位,方可使用element.style.left
如下所示:
给定一个盒子,让其缓慢移动至300px的位置。
代码如下:
<style>
div{
position: absolute;
left: 0;
top: 0;
width: 100px;
height: 100px;
background-color: cyan;
}
</style>
</head>
<body>
<div></div>
<script>
var div = document.querySelector('div');
var timer = setInterval(function(){
if(div.offsetLeft >= 300){
clearInterval(timer);
}
div.style.left = div.offsetLeft + 1 +'px';
},30);
</script>
</body>
运行结果为:

运行成功。
但若同时需对多个元素添加动画,可考虑将其封装为简单动画函数。
二、动画函数简单封装
函数需传递2个参数,即动画对象和移动到的距离。如下所示:
function animate(obj,target){
var timer = setInterval(function(){
if(obj.offsetLeft >= target){
clearInterval(timer);
}
obj.style.left = obj.offsetLeft + 1 +'px';
},30);
}
即可通过调用上述封装函数来实现动画效果。例如,给定两个不同盒子,分别调用动画函数:
<style>
.box1{
position: absolute;
left: 0;
top: 50px;
width: 100px;
height: 100px;
background-color: cyan;
}
.box2{
position: absolute;
left: 0;
top: 155px;
width: 150px;
height: 150px;
background-color: deepskyblue;
}
</style>
<body>
<div class="box1"></div>
<div class="box2"></div>
<script>
function animate(obj,target){
var timer = setInterval(function(){
if(obj.offsetLeft >= target){
clearInterval(timer);
}
obj.style.left = obj.offsetLeft + 1 +'px';
},30);
}
var box1 = document.querySelector('.box1');
var box2 = document.querySelector('.box2');
animate(box1,300);
animate(box2,400);
</script>
</body>
效果为:

成功实现了动画的效果。
但上述封装的动画函数仍有问题,每次调用都会开辟一块内存空间,造成内存资源浪费,且每次调用的动画函数均以同一名字命名,易引起歧义,因此可为不同元素使用不同的定时器(各自专属自己的定时器)。
三、 动画函数给不同元素记录不同定时器
核心原理:利用 JS 是一门动态语言,可方便地给当前对象添加属性。
通过给对象添加属性的方法给不同元素添加定时器,可将其进行如下封装:
function animate(obj,target){
obj.timer = setInterval(function(){
if(obj.offsetLeft >= target){
clearInterval(obj.timer);
}
obj.style.left = obj.offsetLeft + 1 +'px';
},30);
}
当然,若想让某个元素在执行一系列操作后才实现动画效果,可给其添加特定事件,并将函数调用写在事件中,
以第一个例子为例,给它添加点击事件,当点击按钮后,才让这个盒子发生移动:
var box1 = document.querySelector('.box1');
var btn = document.querySelector('button')
btn.addEventListener('click',function(){
animate(box1,300);
})
效果为:

效果实现,但如果一直点击按钮,会出现什么情况呢?

我们会发现,当不断点击按钮时,盒子运行速度会越来越快,这是因为同时开启了太多定时器。如何解决呢?方案是让元素先清除以前的定时器,只保留一个定时器执行,因此,可在函数的最上面添加一个清除定时器的操作。代码为:
function animate(obj,target){
clearInterval(obj.timer);
obj.timer = setInterval(function(){
if(obj.offsetLeft >= target){
clearInterval(obj.timer);
}
obj.style.left = obj.offsetLeft + 1 +'px';
},30);
}
var box1 = document.querySelector('.box1');
var btn = document.querySelector('button');
btn.addEventListener('click',function(){
animate(box1,300);
})
此时的运行效果为
成功实现。
通过上述一系列操作,可发现所实现的动画均为匀速,为使效果更佳,可让动画以缓动速度运行。

四、缓动效果原理
缓动动画即让元素运动速度有所变化,最常见的是让速度慢慢停下来。
- 思路:让盒子每次移动的距离慢慢变小,速度就会慢慢落下来。
- 核心算法: (目标值 - 现在的位置 ) / 10 作为每次移动的距离 步长
- 停止的条件: 让当前盒子位置等于目标位置就停止定时器
注意步长值需取整
以上个例子为例,当点击按钮时,让元素以缓动速度移动,可将封装的动画函数改为:
function animate(obj,target){
clearInterval(obj.timer)
obj.timer = setInterval(function(){
var step = (target - obj.offsetLeft)/10;
if(obj.offsetLeft == target){
clearInterval(obj.timer);
}
obj.style.left = obj.offsetLeft + step +'px';
},30);
}
实现效果为:

这样的效果是否更好看了呢?但来检查一下我们的元素具体移动了多大距离,是否刚好到目标值300px的位置呢?

通过检查发现,我们的元素并未到指定位置,这是因为我们的步长公式有问题,进行除法运算时,可能会有小数,从而导致位置偏差,因此需对步长公式进行取整操作,由于元素是向前运动(正方向),所以采用的策略是向上取整:
var step = Math.ceil((target - obj.offsetLeft)/10);
此时我们在来看看最终到达的目标位置是:

此时就刚好到达了目标位置。
五、 动画函数在多个目标值之间移动
但如果我们的步长为负呢?
举个例子,现在有一个盒子,给其添加两个按钮,一个让元素移动到400px的位置,一个让元素移动到700px:
function animate(obj,target){
clearInterval(obj.timer)
obj.timer = setInterval(function(){
var step = Math.ceil((target - obj.offsetLeft)/10);
if(obj.offsetLeft >= target){
clearInterval(obj.timer);
}
obj.style.left = obj.offsetLeft + step +'px';
},30);
}
var box1 = document.querySelector('.box1');
var btn = document.querySelectorAll('button')
btn[0].addEventListener('click',function(){
animate(box1,400);
})
btn[1].addEventListener('click',function(){
animate(box1,700);
})
实现效果为:

此时发现,当正向运动时,元素可精确到达目标位置,且元素也能实现在两个像素间移动的效果,但向后退时达到的位置却不是目标位置,这是因为我们的元素在倒退时,属于反向运动,这时也应让步长向反向长的位置取整,即向下取整。
这时,应对步长条件进行判断,如果步长大于零,则向上取整,如果步长小于零,则向下取整,调整后的步长公式为:
var step =(target - obj.offsetLeft)/10;
step > 0 ? Math.ceil(step) : Math.floor(step);
此时再来看看效果:

问题就解决了。
但此时只是简单实现了一个元素在两个位置的移动,若想在它移动后改变颜色,该如何操作呢?即可通过给动画函数添加回调函数 来实现。
六、动画函数添加回调函数
回调函数原理:函数可以作为一个参数。将这个函数作为参数传到另一个函数里面,当那个函数执行完之后,再执行传进去的这个函数,这个过程就叫做回调。
回调函数写的位置:定时器结束的位置。
具体实现代码为:
function animate(obj,target,callback){
clearInterval(obj.timer)
obj.timer = setInterval(function(){
var step =(target - obj.offsetLeft)/10;
step = step > 0 ? Math.ceil(step) : Math.floor(step);
if(obj.offsetLeft == target){
clearInterval(obj.timer);
if(callback){
callback();
}
}
obj.style.left = obj.offsetLeft + step +'px';
},30);
}
var box1 = document.querySelector('.box1');
var btn = document.querySelectorAll('button');
btn[0].addEventListener('click',function(){
animate(box1,400,function(){
box1.style.backgroundColor = 'pink';
});
})
btn[1].addEventListener('click',function(){
animate(box1,700,function(){
box1.style.backgroundColor = 'red';
});
})
实现效果为:

以上就是动画函数的封装,在具体使用时,即可将其封装成一个js文件,需要时即可直接引用。
到此这篇关于JavaScript动画函数封装详解的文章就介绍到这了,更多相关JavaScript动画函数封装内容请搜索本站以前的文章或继续浏览下面的相关文章希望大家以后多多支持本站!
JS处理含Emoji或中文的字符串转数组乱码问题
2026-11-05 16:12:28
JS怎么使用展开运算符合并对象
2026-11-05 14:42:18
ES6+环境下判断JS对象为空的简洁写法
2026-11-05 12:12:23
怎么通过replacer函数自定义JS中JSON的格式化输出
2026-11-05 11:19:44
JS正则表达式基础:字面量与RegExp构造函数的区别及常用元字符
2026-11-05 11:16:58
