JS轮播图核心:从匀速到缓动函数的封装实现

作者:半夏

时间:2026-10-21

浏览:169

本文详细解析轮播图中缓动函数的封装过程。从基础的匀速运动需求出发,讲解如何利用setInterval和offsetLeft实现盒子移动。随后进阶至变速运动(缓动),通过计算步长实现先快后慢的效果,并展示如何封装支持多属性同时运动的完整动画函数,为构建流畅的轮播图提供核心代码逻辑。

文章目录

轮播图与缓动函数的关系

轮播图的核心实现依赖于缓动函数的封装。如果把轮播图比作一辆行驶的汽车,那么缓动函数就是驱动它的引擎。本文将由浅入深,引导读者逐步封装出适用于轮播图的自定义缓动函数。

我们从具体的业务需求入手,首先设定一个基础场景:

基础匀速运动实现

1、如何实现一个页面中的元素从初始位置匀速向右移动至200px处?

逻辑分析:

1)首先需获取元素的当前位置,这可以通过offsetLeft属性获得;

2)实现匀速运动,在JavaScript中通常依赖setInterval定时器;

3)要使元素向右移动,需持续改变其相对于左侧起始点的距离。虽然可以通过margin-left或绝对定位的left属性实现,本文选择修改绝对定位的left值;

4)当元素到达距离起点200px的位置时,需使用clearInterval停止定时器以结束运动。

以下是基础运动的代码实现:


<!DOCTYPE html>
<html lang="en">
 <head>
 <meta charset="UTF-8" />
 <title>Document</title>
 <style type="text/css">
  * {
  margin: 0;
  padding: 0;
  }
  div {
  position: absolute;
  top: 50px;
  width: 100px;
  height: 100px;
  background-color: red;
  }
  input {
  width: 100px;
  height: 30px;
  color: #fff;
  background-color: yellowgreen;
  }

 </style>
 </head>

 <body>
 <div></div>
 <input type="button" value="移动到200" />


 <script type="text/javascript">
  // 获取到元素(这里有个小细节,如果给元素设置了id名,即便不使用获取元素的方法,也能通过这个id名获取到元素哦~~大家可以自己尝试一下)
  var btn = document.querySelector('input'),
   dv = document.querySelector('div');
  // 添加点击事件
  btn.addEventListener('click',function() {
  var timer = null,// 保存定时器
   currentDistance = dv.offsetLeft, // 当前离父盒子的距离
   step = 8,// 每次改变的距离
   target = 200;// 目标距离
  timer = setInterval(function() {
   currentDistance += step;// 当前距离 = 上一个当前距离 + 改变的距离
   if((target - currentDistance) < step) { 
   currentDistance = target; // 如果目标距离与当前距离的差小于了要改变的距离,这时候我们就直接让当前距离等于目标距离,防止盒子停下来的时候有误差
   clearInterval(timer); // 清楚定时器
   timer = null; // 将timer解绑,释放内存
   }
   dv.style.left = currentDistance + 'px'; // 最核心的一步,改变盒子的left为当前距离
  },17)
  })
 </script>
 </body>
</html>


运动函数的封装与复用

2、在实现初步运动效果后,我们升级需求:

当盒子运动到200px位置后,如何让它继续运动至400px位置?

逻辑分析:

1)此时需要两个按钮,分别触发运动至200px和400px的动作;

2)尽管涉及两个运动目标,但本质都是“从一点移动到另一点”,因此考虑将第一步的运动逻辑封装为独立函数,以便复用。

相关代码如下:


<!DOCTYPE html>
<html lang="en">
<head>
 <meta charset="UTF-8" />
 <title>Document</title>
 <style type="text/css">
 * {
 margin: 0;
 padding: 0;
 }
 div {
 position: absolute;
 top: 50px;
 width: 100px;
 height: 100px;
 background-color: red;
 }
 input {
 width: 100px;
 height: 30px;
 color: #fff;
 background-color: yellowgreen;
 }

 </style>
</head>

<body>
 <div></div>
 <input type="button" value="移动到200" />
 <input type="button" value="移动到400" />
 <script type="text/javascript">
 // 封装函数,盒子和目标距离都是不确定的,我们可以将他们作为参数传递。
 function animation(tag,target) {
 var timer = null,
  currentDistance = tag.offsetLeft,
  step = 5;
 step = currentDistance < target? step: -step;// 判断step的正负,200到400时是递增,400到200时是递减
 timer = setInterval(function() {
 if(Math.abs(currentDistance - target) > Math.abs(step)) { // 这里判断条件也要略作改动,使用绝对值进行比较
  currentDistance += step; /
  tag.style.left = currentDistance + 'px';
 }else {
  tag.style.left = target + 'px' // 当当前距离与目标距离之间的差值小于step改变的距离时,我们直接让盒子移动到目标距离。
  clearInterval(timer);
  timer = null;
 }
 },17)
 }
 var btns = document.querySelectorAll('input'),
 dv = document.querySelector('div');
 btns[0].addEventListener('click',function() {
 animation(dv,200);
 })
 btns[1].addEventListener('click',function() {
 animation(dv,400);
 })
 </script>
</body>
</html>

变速(缓动)运动逻辑

3、虽然我们已经封装了盒子来回运动的函数,但回顾轮播图的滚动效果,它并非匀速移动,而是呈现“先快后慢”的特征:初始阶段速度较快,接近目标位置时速度逐渐降低。

新需求:实现盒子的缓动(即变速)运动效果。

相关代码如下:


function animation(tag,target) {
 var timer = null;
 timer = setInterval(function() {
 var currentDistance = tag.offsetLeft,
  step = (target - currentDistance) / 5;// 通过目标距离与当前距离的差除以5便达到了我们需要的变速运动,因为step每次定制器执行都要改变,所以放入定时器内
 step = step > 0 ? Math.ceil(step):Math.floor(step);// 这里如果将currentDistance定时器外面声明可以不用写,如果放在定时器内声明,因为offsetLeft取整的特性,要对step进行取整
 if(Math.abs(currentDistance - target) > Math.abs(step)) {
  currentDistance += step;
  tag.style.left = currentDistance + 'px';
 }else {
  tag.style.left = target + 'px'
  clearInterval(timer);
  timer = null;
 }
 },17)

至此,轮播图所需的最基础缓动函数已封装完成。

多属性同时运动的完整方案

此外,这里补充一个功能更完善的缓动函数版本,它支持同时修改多个样式属性。


function perfectAnimate(tag, obj, fn) {// 传三个参数,运动的盒子,对象(可以传多个属性),回调函数(在执行完后可以再执行自定义的功能)
 clearInterval(tag.timer);// 这里将定时器作为tag标签的属性保存,可以多次调用函数清除上一个定时器。
 tag.timer = setInterval(function () {
 var flag = true;
 for (var k in obj) {
       // 因为并不是所有属性都带px单位,所以这里进行判断分别设置 
  if (k == 'opacity') {
  var currentDistance = getStyle(tag, k) * 100,
   target = obj[k] * 100,
   step = (target - currentDistance) / 10;
  step = step > 0 ? Math.ceil(step) : Math.floor(step);
  currentDistance += step;
  tag.style[k] = currentDistance / 100;
  } else if (k == 'zIndex') {
  tag.style[k] = obj[k];
  else {
  var currentDistance = parseInt(getStyle(tag, k)) || 0,
   target = obj[k],
   step = (target - currentDistance) / 10;
  step = step > 0 ? Math.ceil(step) : Math.floor(step);
  currentDistance += step;
  tag.style[k] = currentDistance + 'px';
  }
  if (target != currentDistance) {
  flag = false // 只要还有属性没有运动完成,就不会清楚定时器
  }
 }
 if (flag) {
  clearInterval(tag.timer)
  fn && fn();// 所有定时器走完,这里执行回调函数,短路操作避免不传回调函数也不会报错。
 }
 }, 17)
}

// 获取样式的兼容函数,上面的缓动函数的依赖
function getStyle(tag, attr) {
 if (tag.currentStyle) {
 return tag.currentStyle[attr];
 } else {
 return getComputedStyle(tag, null)[attr];
 }
}

以上就是本文的全部内容,希望能为读者的学习提供助力,也欢迎大家继续支持本站。

实用工具推荐

电子包浆

绿化、压缩、噪点和虚拟水印,自由调节做旧程度。

使用 ›
图片压缩

选择画质和输出格式,预览压缩前后的大小。

使用 ›
相关文章