原生JS实现无缝轮播图:从结构到动画逻辑详解

作者:微光

时间:2026-10-22

浏览:628

本文详细解析使用原生JavaScript实现无缝轮播图的技术方案。内容涵盖HTML结构搭建、CSS定位布局以及核心JS逻辑,重点讲解如何通过添加冗余图片实现无缝切换、利用定时器与步长控制实现平滑淡入动画,以及自动播放与手动交互的协同机制。文章提供完整代码示例及面向对象封装思路,适合前端开发者参考学习。

文章目录

本文详细介绍如何使用原生JavaScript开发一个功能完整的轮播图组件,并提供相关代码供读者参考与学习。

轮播图效果预览

一、效果预览:

轮播图效果预览

受限于上传文件大小限制(2M以下),此处仅展示自动切换的视觉效果:

技术栈与开发思路

二、编写语言

HTML、CSS、原生JS

三、编写思路

HTML结构搭建

(一)HTML部分

1、.slide作为承载所有图片的滑槽容器;

2、.prev用于存放向左切换的箭头按钮,存放向右切换的箭头按钮;

3、pointer作为指示器容器,包含下方五个用于切换的按钮,每个按钮以span元素表示;

4、.m-view作为视窗容器,用于展示当前可见区域,并包含上述所有功能部件;

CSS样式布局

(二)CSS部分

1、.m-view设置为相对定位,使其子元素可以基于它进行绝对定位;

2、.slide、.prev、.next、pointer均使用绝对定位,并调整至合适位置;

3、.slide内的图片水平排列,同时视窗.m-view的宽度设置为仅容纳一张图片的宽度。默认情况下.slide会全部显示,但通过给.m-view设置overflow:hidden属性,隐藏超出部分,从而实现每次仅显示一张图片的效果;

JavaScript核心逻辑

(三)JS部分

1、切换功能:

定义切换函数toggle以执行左切或右切单张图片的操作,该函数包含leftToggle和rightToggle两个子函数,分别负责向左和向右切换一张图片,并将它们分别绑定至.prev和.next按钮的clik事件;

2、切换功能的淡入动画效果

若仅执行一次切换,画面会立即变化且无过渡效果。此处利用定时器和步长将切换细化为多次滑动操作leftStep和rightStep,leftToggle和rightToggle通过多次调用这些滑动操作来完成一次完整切换,从而产生平滑的动画效果;

3、跳转功能

为指示器的每个圆形按钮绑定跳转逻辑,跳转本质上是移动的位置;

4、自动播放

只需设置定时器,每隔固定时间执行一次切换操作即可;

完整代码实现

四、我的代码


<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>轮播图</title>
  <style>
    .m-view,.m-view .slide img{
      position: relative;/*作为绝对定位的父元素*/
      width: 800px;
      height: 600px;
    }
    .m-view{
      overflow: hidden;/*将超出该div的子元素隐藏*/
    }
    .m-view .slide{
      position: absolute;
      width: 8000px;
      height: 600px;
    }
    .m-view .slide img{
      margin-right: -5px;
    }
    .m-view .prev,.m-view .next{
      position: absolute;
      top: 40%;
      font: 60px/60px Microsoft YaHei;
      color: #00BFFF;
    }
    .m-view .prev{
      left: 10px;
    }
    .m-view .next{
      right: 10px;
    }
    .m-view .pointer{
      position: absolute;
      bottom: 40px;
      left: 33%;
    }
    .m-view .pointer span{
      display: inline-block;/*水平排列*/
      width: 40px;
      height: 40px;
      border-radius: 20px;
      margin-right: 10px;
      background-color: #00FF00;
    }
    .m-view .pointer .on{/*点亮当前图片对应的圆圈*/
      background-color: #1E90FF;
    }
  </style>
</head>
<body>
  <div class="m-view">
    <div class="slide" style="left: -800px">
      <img src="../lunbo/5.jpg" alt="4">
      <img src="../lunbo/1.jpg" alt="0">
      <img src="../lunbo/2.jpg" alt="1">
      <img src="../lunbo/3.jpg" alt="2">
      <img src="../lunbo/4.jpg" alt="3">
      <img src="../lunbo/5.jpg" alt="4">
      <img src="../lunbo/1.jpg" alt="0">
    </div>
    <div class="prev">&lt;</div>
    <div class="next">&gt;</div>
    <div class="pointer">
      <span class="button on" index="0"></span>
      <span class="button" index="1"></span>
      <span class="button" index="2"></span>
      <span class="button" index="3"></span>
      <span class="button" index="4"></span>
    </div>
  </div>
  <script type="text/javascript">
    var view=document.getElementsByClassName('m-view')[0];
    var slide=document.getElementsByClassName('slide')[0];
    var prev=document.getElementsByClassName('prev')[0];
    var next=document.getElementsByClassName('next')[0];
    var button=document.getElementsByClassName('button');
    var curIndex=0;//当前图片的索引位置
    var toggled=true;//是否正在切换,true表明切换已完成,此时才能切换
    /* Toggle函数实现切换一张图片的功能 */
    function Toggle () {
      var TIMER=50;//滑动一次所用的时间,它是setInterval的第二个参数
      var time=800;//每切换一张图片总共用的时长
      var times=time/TIMER;//每切换一张图片需滑动的次数
      var stepLenth=800/times;//每次滑动的步长
      var leftToggle=function () {
        var t1=times;
        function leftStep(){
          slide.style.left=parseInt(slide.style.left)+stepLenth+"px";
          t1--;
          if (!t1) {
            clearInterval(interval);
            curIndex--;
            if (curIndex<0) {
              slide.style.left=parseInt(slide.style.left)-4000+"px";
              curIndex=4;
            };
            toggled=true;
          };
        };
        if (toggled==true) {
          toggled=false;
          button[curIndex].className="button";
          if (curIndex!=0) {
            button[curIndex-1].className="button on";
          }else{
            button[curIndex+4].className="button on";
          }
          var interval=setInterval(leftStep,TIMER);
        };
      };
      var rightToggle=function () {
        var t2=times;
        function leftStep(){
          slide.style.left=parseInt(slide.style.left)-stepLenth+"px";
          t2--;
          if (!t2) {
            clearInterval(interval);
            curIndex++;
            if (curIndex>4) {
              slide.style.left=parseInt(slide.style.left)+4000+"px";
              curIndex=0;
            };
            toggled=true;
          };
        };
        if (toggled==true) {
          toggled=false;
          button[curIndex].className="button";
          if (curIndex!=4) {
            button[curIndex+1].className="button on";
          }else{
            button[curIndex-4].className="button on";
          };
          var interval=setInterval(leftStep,TIMER);
        };
      }
      this.leftToggle=leftToggle;//输出对外的接口
      this.rightToggle=rightToggle;
    };
    var toggle=new Toggle();
    prev.onclick=function () {
      toggle.leftToggle();
    };
    next.onclick=function () {
      toggle.rightToggle();
    };
    /* 点击圆圈跳转功能 */
    for (var i = 0; i < button.length; i++) {
      button[i].onclick=function () {
        var newIndex=parseInt(this.getAttribute("index"));
        if (newIndex!=curIndex) {
          var distance=-800*(newIndex-curIndex);
          slide.style.left=parseInt(slide.style.left)+distance+"px";
          button[curIndex].className="button";
          button[newIndex].className="button on";
          curIndex=newIndex;
        };
      };
    }
    /* 自动播放功能,鼠标移上去停止播放,移开再次播放 */
    var intervalo=setInterval(toggle.rightToggle,3000);
    view.onmouseover=function () {
      clearInterval(intervalo);
    }
    view.onmouseout=function () {
      intervalo=setInterval(toggle.rightToggle,3000);
    }
  </script>
</body>
</html>

开发总结与优化建议

五、一些总结

1、本次采用了面向对象和封装的思路,这是因为个人体会到确实面向对象的设计能使代码编写思路更加清晰,还能够免去很多冗余重复的代码,也尝试过其他书写思路,但都会使代码变得不太直观;要注意的一点就是封装后要向外提供接口,且如果是封装在一个函数中,需要实例化一个对象才能调用;

2、在中设置了一个内联样式,这是因为在后面要获取并改变它的left属性,如果不采用内联样式的方法,将无法设置;因为初始时.style.left只能获取内联样式,即使采用内部样式和外部样式也会使得获取的值为undefined。当然,肯定也可以采用其他方法,但是似乎其他方案都更为复杂一些,没找到更简便的方法。

3、在前后各多放置一张图片的作用: 比如,当前是图片1,现在向左切换,可以和其他位置一样先执行统一的左移操作,这时视窗显示额外放置的图片5,再将整体左移使真正的图片5显示在视窗中,这样是先出现了动画效果再“暗中移动”了,就好像没移动一样,真正做到了无缝切换,逻辑也很简单;如果不放置额外的图片,就需要先将视窗左移,使图片5显示在视窗中,这样动画效果难以设置。

4、在获取每个span在它父元素的索引位置时,采用了getAttribute获取自定义的index属性的方法,其他方法肯定也有不少,但是肯定不能在循环中把i的值直接当成span的索引位置。

5、在跳转功能中,如果要跳转的正是当前的页面,应该什么也不做,这样可以优化性能 。

6、在点击左右箭头切换时,先判断上一次动画是否完成,没完成就不切换,这样可以优化性能,否则连续点击可能导致卡顿、切换效果不佳。

7、代码似乎还有可以优化的地方;

8、这个只是制作了一个轮播图,接下来考虑做一个轮播组件,似乎难度要大些,还有3D的轮播效果也想要尝试下了。

PS:感兴趣的朋友还可以将上述代码中的图片替换成网络图片,再使用如下在线工具在线测试运行效果:

HTML/CSS/JavaScript代码运行工具 http://tools.jb51.net/code/HtmlJsRun

在线HTML/CSS/JavaScript前端代码调试运行工具 http://tools.jb51.net/code/WebCodeRun

更多关于JavaScript相关内容感兴趣的读者可查看本站专题:《JavaScript图片操作技巧大全》、《JavaScript切换特效与技巧总结》、《JavaScript运动效果与技巧汇总》、《JavaScript动画特效与技巧汇总》、《JavaScript错误与调试技巧总结》、《JavaScript数据结构与算法技巧总结》、《JavaScript遍历算法与技巧总结》及《JavaScript数学运算用法总结》

希望本文所述对大家JavaScript程序设计有所帮助。

实用工具推荐

电子包浆

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

使用 ›
图片压缩

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

使用 ›
相关文章