JavaScript 实现轮播图原理与完整实例解析

作者:像素漫游

时间:2026-10-24

浏览:200

本文详细解析了使用原生 JavaScript 实现轮播图的核心原理,涵盖容器布局、动态宽度计算、导航栏交互及平滑动画逻辑。通过实例代码演示如何利用定时器实现自动播放与手动切换,并解决循环播放时的位置跳转问题,帮助开发者掌握前端交互组件的开发技巧。

文章目录

制作轮播图需先理解其核心原理:如何实现图片从右向左的滑动效果?类比电影胶片的工作机制,我们可以构建一个固定大小的显示容器(投影区),并在其中放置一个包含所有图片的列表(底片)。通过控制该列表向左移动,即可实现图片的轮播展示效果。

创建容器与列表结构

创建容器与列表结构

首先,需要构建一个主块作为整体容器及显示区域。


<div id="out">
 <ul id="imgList">
 <li><img src="pto/many.jpg" ></li>
 <li><img src="pto/hello.jpg" ></li>
 <li><img src="pto/timg.jpg" ></li>
 <li><img src="pto/zhenjing.jpg"></li>
 </ul>
</div>

初始状态下,图片垂直堆叠成一列,且容器位置未定。此时需添加基础样式以明确布局。

为容器开启定位并设置居中,同时适当增大容器尺寸并添加背景色以便观察位置。本实验图片统一宽高比为 500*431,因此 div 容器的宽高比设置为 520*451。

移除列表的默认样式,使列表元素横向排列显示。


*{
 margin: 0;
 padding: 0;
 }
 #out{
  width:520px;
  height:451px ;
  background-color: #00bcd4;
 position: relative;
 margin: 50px auto;
 /*overflow: hidden;*/
 /*剪掉我们不需要的部分,为了方便调试注掉了*/
 }

 #imgList{
  list-style: none;
  position: absolute;
  /* left: -520px; */
 }
 #imgList li{
  float:left;
  margin: 10px;
}

尝试使用浮动布局后,图片仍保持垂直排列,原因是列表宽度不足。因此需要扩大列表宽度。由于图片数量可能动态变化,列表宽度不应固定,而应通过 JavaScript 动态计算。每增加一张图片,列表宽度增加 520px。


window.onload=function () {
 // 动态的ul长度
 var imgList = document.getElementById("imgList");
 var imgArr = document.getElementsByTagName("img");
 imgList.style.width=520*imgArr.length+"px";
 }//onload

当装载图片的列表(ul)向左偏移 -520px 时,即可切换显示下一张图片。

添加导航栏功能

轮播图通常具备定时自动切换功能,但若用户在图片切换瞬间离开视线,体验将大打折扣。为了提升交互性,完整的轮播图应提供手动切换前后图片的按钮或导航条。本例中使用超链接来实现这一功能。


<div id="navDiv">
 <a href="javascript:;"></a>
 <a href="javascript:;"></a>
 <a href="javascript:;"></a>
 <a href="javascript:;"></a>
</div>

当 ul 开启绝对定位后,导航栏因失去内容支撑而收缩至左上角。我们需要使每个超链接彼此分离,手动撑开空间,并将其调整至靠下位置(如下方正中或右侧)。同时,通过调整透明度降低导航栏的视觉吸引力,确保图片仍是视觉主体。位置的具体调整仍由 JavaScript 处理以方便扩展。


 }
 #navDiv{
  position: absolute;
  bottom: 15px;
 }
 #navDiv a{
  float: left;
  width: 15px;
  height: 15px;
  background-color: #89ff00;
  margin: 0 5px;
  opacity: 0.5;
 }

//动态导航居中
 var navDiv = document.getElementById("navDiv");
 var out = document.getElementById("out");
 
 //将纵向剩余距离分到导航左右达到居中效果
 //不除以二就会变成右对齐
 //不要忘了单位,嗯。。可能只有我会忘吧
 navDiv.style.left = (out.clientWidth - navDiv.clientWidth)/2+"px";

完善导航交互功能

仅有一个 15px 见方的小块无法提供足够的用户反馈。当前图片的位置应通过导航栏明确指示:当鼠标悬停在导航项上时,需有视觉反馈提示用户该链接可点击,且点击后可切换至对应图片。


#navDiv a:hover{
  background-color: red;
  /* 鼠标移入效果*/
 /* 内联样式的优先级很高注意不要被覆盖失效*/
 }

//定位效果
var allA = document.getElementsByTagName("a");
var index = 0;
 allA[index].style.backgroundColor="black";

 //点击导航效果
 //使用块级作用域let,不然i会是同一个数
 for(let i=0;i<allA.length;i++){
  allA[i].onclick=function () {
  imgList.style.left=-520*i+"px";

  //清除内联样式,使得css文件能生效
  allA[index].style.backgroundColor="";
  index=i;
  allA[index].style.backgroundColor="black";
  }
 }

实现动画效果

添加动画的原因在于提升视觉体验(因为这样更酷)。

没有过渡动画的轮播失去了“轮播”的意义。虽然直接修改位置属性即可切换图片,但为了追求流畅的视觉效果,动画才是轮播图的核心所在。

主要思路是利用定时器将原本一步完成的位移分解为多次小步位移,当到达指定位置时关闭定时器。

需要注意的关键问题

每次移动的距离可能与图片尺寸存在除余误差,导致停止位置不准确(偏大或偏小)或无法精确停止。这种微小误差会随时间累积。此外,在开启新定时器前必须关闭上一个计时器,否则在动画未完成时点击其他按钮会导致动画混乱(鬼畜现象)。


 //点击导航效果
 for(let i=0;i<allA.length;i++){
  allA[i].onclick=function () {
  move(imgList,-520*i,10);
  // imgList.style.left=-520*i+"px";
  //换掉这个很low的过场
  allA[index].style.backgroundColor="";
  index=i;
  allA[index].style.backgroundColor="black";
  }
 }
 
function move(obj,target,speed) {//元素;目标位置;速度
  //每次触发事件关闭上一个定时器
  //这里是重点,你可以去掉这一句后随意点一下关差效果
  clearInterval(obj.timer);
  
  var current = parseInt(window.getComputedStyle(obj,null).left);
  //获得当前位置
  //判断运动方向
  if(target<current){
  speed = -speed;
  }
  //定时器标识
  obj.timer = window.setInterval(function () {
  //m每次开始获取一下当前位置
  var oldValue = parseInt(window.getComputedStyle(obj,null).left);
  //移动并在指定位置停下
  var newValue = oldValue + speed;
  //调整一下停止位置,小的误差会随时间无限放大
  if((speed < 0 && newValue < target)||(speed > 0 && newValue > target)){
   newValue = target;
  }
  imgList.style.left =newValue+"px";
  if(newValue == target){
   clearInterval(obj.timer);
  }

  },30);

 }

change();
 //自动轮播
 //一个定时器用于定时调用
 function change() {
  setInterval(function () {
  index++;
  index=index % imgArr.length ;
  console.log(imgArr.length);
  console.log(index);
  move(imgList,-520*index,20);
  for(let i=0;i<allA.length;i++){
   allA[i].style.backgroundColor="";
   allA[index].style.backgroundColor="black";
  }
  },3000);
 }

上述实现已具备轮播的基本功能,但在从最后一张图片切换回第一张图片时,列表会向左滚动经过所有图片,视觉效果不佳。为了实现无缝循环播放,需采用特殊技巧。

假设我们要对图片 a 和图片 b 进行轮播演示。

我们可以在列表末尾插入一张与图片 a 相同的副本。当轮播完图片 b 进入“第三张”(即副本 a)时,给用户造成仍在第一张图片的错觉。当这张副本图片完成轮播后,瞬间将列表位置重置至真正的第一张图片,从而实现无缝循环。此策略可称为“瞒天过海”。

创建容器与列表结构

对于轮播图,掌握其原理至关重要。即便不考虑使用框架,仅用 jQuery 实现轮播图所需的精力也远少于纯手写原生代码。

完整代码示例


<!DOCTYPE html>
<html lang="en">
<head>
 <meta charset="UTF-8">
 <title>Title</title>
 <style>
 *{
  margin: 0;
  padding: 0;
 }
 #out{
  width:520px;
  height:451px ;
  margin: 50px auto;
  background-color: #00bcd4;
  position: relative;
  overflow: hidden;
 }

 #imgList{
  list-style: none;
  position: absolute;
 }
 #imgList li{
  float:left;
  margin: 10px;
 }
 #navDiv{
  position: absolute;
  bottom: 15px;
 }
 #navDiv a{
  float: left;
  width: 15px;
  height: 15px;
  background-color: #89ff00;
  margin: 0 5px;
  opacity: 0.5;


 }
 #navDiv a:hover{
  background-color: red;
 /* 内联样式的优先级很高在触发一次后覆盖失效*/
 }

 </style>
</head>
<body>
<div id="out">
 <ul id="imgList">
 <li><img src="pto/many.jpg" ></li>
 <li><img src="pto/hello.jpg" ></li>
 <li><img src="pto/timg.jpg" ></li>
 <li><img src="pto/zhenjing.jpg"></li>
 <li><img src="pto/many.jpg" ></li>
 </ul>
 <div id="navDiv">
 <a href="javascript:;" ></a>
 <a href="javascript:;" ></a>
 <a href="javascript:;" ></a>
 <a href="javascript:;" ></a>
 </div>
</div>
<script>
 window.onload=function () {
 // 动态的ul长度
 var imgList = document.getElementById("imgList");
 var imgArr = document.getElementsByTagName("img");
 imgList.style.width=520*imgArr.length+"px";

 //动态导航居中
 var navDiv = document.getElementById("navDiv");
 var out = document.getElementById("out");
 navDiv.style.left = (out.clientWidth - navDiv.clientWidth)/2+"px";

 //定位效果
 var allA = document.getElementsByTagName("a");
 var index = 0;
 allA[index].style.backgroundColor="black";

 //点击导航效果
 for(let i=0;i<allA.length;i++){
  allA[i].onclick=function () {
  move(imgList,-520*i,20);
  setA();
  // imgList.style.left=-520*i+"px";
  // allA[index].style.backgroundColor="";
  // index=i;
  // allA[index].style.backgroundColor="black";
  }
 }

 // 动画效果
 function move(obj,target,speed,callback) {//元素;目标位置;速度;回调函数
  clearInterval(obj.timer);
  var current = parseInt(window.getComputedStyle(obj,null).left);
  //获得当前位置
  //判断运动方向
  if(target<current){
  speed = -speed;
  }
  //定时器标识
  obj.timer = window.setInterval(function () {
  //m每次开始获取一下位置
  var oldValue = parseInt(window.getComputedStyle(obj,null).left);
  //移动并在指定位置停下
  var newValue = oldValue + speed;
  //调整一下停止位置,小的误差会随时间无限放大
  if((speed < 0 && newValue < target)||(speed > 0 && newValue > target)){
   newValue = target;
  }
  imgList.style.left =newValue+"px";
  if(newValue == target){
   clearInterval(obj.timer);
   callback();
  }

  },30);

 }
 change();
 //自动轮播
 //一个定时器用于定时调用
 function change() {
  setInterval(function () {
  index++;
  index=index % imgArr.length ;
  move(imgList,-520*index,20,function () {
   if(index>=imgArr.length-1 ){
   imgList.style.left =0;
   }
   setA();
  });

  },3000);
 }
 function setA() {
  for(let i=0;i<allA.length;i++){
  allA[i].style.backgroundColor="";
  allA[index].style.backgroundColor="black";

  }
 }
 }//onload
</script>
</body>
</html>

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持本站。

实用工具推荐

电子包浆

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

使用 ›
图片压缩

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

使用 ›
相关文章