头图
「公众号:追梦web前端课堂」

我们以京东首页轮播图为例来讲解

第一步:需求分析

  • 自动循环轮播
  • 过渡动画为渐隐渐现
  • 控制流结构 有  圆点按钮 和 上一张、下一张 按钮
  • 鼠标经过圆点切换到当前图片并且轮播停止,鼠标离开轮播开始。

第二步:写出轮播图的静态页面结构

  • 轮播图一般为列表结构,可以用ul
  • 轮播图的图片位置是在重叠在一起的,可以用 css 绝对定位
  • 在轮播图容器中只能显示一张图片,其他图片都隐藏。由于 轮播动画是渐隐渐现。所以可以把所有的图片透明度设为0,通过 使用修饰符来改变图片的状态。
  • 样式名 可以采用 BEM 命名

HTML结构


<!--轮播容器-->
<div id="my-slider" class="slider-list">
  <!-- 图片列表 -->
  <ul>
    <li class="slider-list__item slider-list__item--active">
      ![](./img/1.jpg)
    </li>
    <li class="slider-list__item">
      ![](./img/2.jpg)
    </li>
    <li class="slider-list__item">
      ![](./img/3.jpg)
    </li>
  </ul>
</div>```
<!--轮播容器-->
<div id="my-slider" class="slider-list">
 <!-- 图片列表 -->
 <ul>
 <li class="slider-list__item slider-list__item--active">
 <img src="./img/1.jpg" alt="" class = "slider-list__img">
 </li>
 <li class="slider-list__item">
 <img src="./img/2.jpg" alt="" class ="slider-list__img">
 </li>
 <li class="slider-list__item">
 <img src="./img/3.jpg" alt="" class = "slider-list__img">
 </li>
 </ul>
</div>

CSS代码

* {
 margin: 0;
 padding: 0;
 }
 ul {
 list-style: none;
 }
 #my-slider {
 width: 600px;
 height:400px
 }
 
 /* 图片列表 */
 
 .slider-list {
 position: relative;
 height: 100%;
 
 }
 .slider-list ul {
 height: 100%;
 }
 .slider-list__img {
 width: 100%;
 height: 100%;
 }
 
 .slider-list__item {
 position: absolute;
 opacity: 0;
 }
 
 .slider-list__item--active {
 transition: opacity 1s;
 opacity: 1;
 }

第三步:结构写好之后,开始具体的实现

实现思路:

  • 主要通过 修饰符 slider-list__item--active 来改变图片的显示隐藏状态。( 显示图片 我们可以 添加 该修饰符, 隐藏图片 我们可以移除该修饰符)
  • 切换动画 通过  transition 来实现
  • 循环轮播 要实现的 一个小难点,就是如何 从最后一张 过渡到 第1张。
  • 那 最笨的办法就是 当 轮播图片到最后一张的时候,把 当前图片的位置强制设置为第一张。
  • 当然了,我们还有更好的办法来实现。这里是有1个小技巧,我们可以通过 余数定理 来实现。假如 轮播图片是4张。那么 轮播图是以 4 为周期循环的。任何数 除以 4 的余数  一定在 4 的范围内。而余数代表是当前 图片的位置。知道了 图片的位置,就可以得到 当前图片的节点,那么我们就可以很容易的操作 图片的状态了。
  • 这里有2个公式,大家可以套用
  • 上一张图片的下标 =  ( 当前图片的下标 - 1 + 图片的个数 ) %  图片的个数
  • 下一张图片的下标=   ( 当前图片的下标 + 1 ) %  图片的个数

JS代码


document.addEventListener("DOMContentLoaded",(e) => {
 // 获取轮播容器
 const container = document.getElementById("my-slider");
 // 获取轮播的图片集合
 const items = container.querySelectorAll(".slider-list__item");
 // 定时器变量
 let timer = null;
 // 多长时间轮播一张图片
 const interval = 2000;
 // 获取当前的图片dom节点
 function getCurrentItem(){
 return container.querySelector(".slider-list__item--active")
 }
 // 获取当前的图片位置
 function getCurrentItemIndex(){
 return Array.from(items).indexOf(getCurrentItem())
 }
 /**
 * 获取控制点的位置
 * @param  {String} target  鼠标经过的节点
 * @return {Number}  鼠标经过节点的位置
 */
 function getControllerIndex(target){
 const nextItemIndex = Array.from(buttons).indexOf(target);
 return nextItemIndex
 } 
 
 //下一张图片
 function next() {
 // 获取下一张图片的位置
 const nextItemIndex = ( getCurrentItemIndex() + 1) % items.length;
 // 切换
 slideTo(nextItemIndex)
 }
 /**
 * 切换到指定位置的图片
 * @param  {number} index 图片的位置
 * @return {undefined} 
 */
 function slideTo( index ) {
 // 获取当前展示的图片 节点
 const currentItem = getCurrentItem(),
 // 获取下一次要展示的图片节点
 nextItem = items[index];
 // 隐藏 当前的图片
 currentItem.classList.remove("slider-list__item--active")
 // 显示 指定 index 的图片
 nextItem.classList.add("slider-list__item--active")
 }
 //自动轮播
 function autoStart() {
 timer = setInterval(function(){ 
 // 切换到下一张
 next()
 },interval)
 }
 //开始轮播
 autoStart()
});

到此,图片轮播的核心功能就实现了。

接下来 我们来实现 控制结构 小圆点 和  上一张/下一张按钮的功能

首先来实现 上一张/ 下一张 的 功能

分析

  • 当 点击 上一张  或者 下一张 的时候  需要  先 停止 自动轮播 、 然后 切换到下一张  再开启自动轮播
  • 注意 这里 需要用到节流函数(我这里没有实现,大家上网查一下
  • 由于 我是用的 a 标签来模拟的按钮,所以 绑定事件的时候一定要阻止 a标签的默认事件

在轮播容器中 增加  上一张/下一张的结构

HTML结构

<!--上一张-->
<a href="" class="slide-list__previous"></a>
<!--下一张-->
<a href="" class="slide-list__next"></a>

CSS代码

/* 上一张 下一张 样式 */
 .slide-list__next,
 .slide-list__previous {
 position: absolute;
 top: 50%;
 width: 30px;
 height: 35px;
 background: rgba(0, 0, 0, 0.5);
 transform: translateY(-50%);
 color: white;
 text-align: center;
 line-height: 35px;
 text-decoration: none;
 
 }
 
 .slide-list__next {
 right: 0;
 
 }
 
 .slide-list__previous::before {
 content: "<";
 }
 
 .slide-list__next::before {
 content: ">";
 }

JS代码

// 获取上一张按钮
 const previousButton =  container.querySelector(".slide-list__previous")
 // 获取下一张按钮
 const nextButton =  container.querySelector(".slide-list__next") 
 
 // 给上一张按钮绑定事件
 previousButton.addEventListener("click",(e)=>{
 stop()
 previous()
 autoStart()
 e.preventDefault()
 })
 // 给下一张按钮绑定事件
 nextButton.addEventListener("click",(e)=>{
 stop()
 next()
 autoStart()
 e.preventDefault()
 })

接下来实现 圆点的功能

HTML结构

 <!-- 新增小圆点 -->
 <div class="slider-list__controller">
 <span class="slider-list__controller-button slider-list__controller-button--active"></span>
 <span class="slider-list__controller-button"></span>
 <span class="slider-list__controller-button"></span>
 <span class="slider-list__controller-button"></span>
 </div>

CSS代码

/* 新增小圆点样式 */
.slider-list__controller {
 position: absolute;
 left: 20px;
 bottom: 30px;
}
.slider-list__controller-button {
 display: inline-block;
 width: 10px;
 height: 10px;
 background: rgba(255, 255, 255,.4);
 border-radius: 50%;
}
.slider-list__controller-button--active {
 background: orange;
}

分析:

  • 当自动轮播开始的时候或者点击上一张/下一张按钮时,图片的切换 和 小圆点的切换 要 一一 对应,并且当前小圆点要高亮显示。
  • sliderTo 函数中 也需要增加 小圆点高亮的逻辑

JS代码

//获取 小圆点 controller  容器
 const controller = container.querySelector(".slider-list__controller");
 // 获取 小圆点集合
 const buttons = controller.querySelectorAll(".slider-list__controller-button");
 // 鼠标经过事件停止自动轮播, 并切换到当前位置
 controller.addEventListener("mouseover",(e)=>{
 const nextItemIndex = getControllerIndex(e.target)
 // 如果存在
 if(nextItemIndex > -1){
 // 停止轮播
 stop()
 slideTo(nextItemIndex)
 }
 })
 // 鼠标离开时候启动 自动轮播
 controller.addEventListener("mouseout",(e)=>{
 const nextItemIndex = getControllerIndex(e.target)
 //如果存在
 if(nextItemIndex > -1){
 // 自动播放
 autoStart()
 }
 })
function slideTo( index ) {
 // 获取当前展示的图片 节点
 const currentItem = getCurrentItem(),
 // 获取下一次要展示的图片节点
 nextItem = items[index],
 // 获取 当前展示 圆点的 节点
 currentButton = buttons[getCurrentItemIndex()],
 // 获取 下一次要展示的圆点节点
 nextButton = buttons[index]
 // 隐藏 当前的图片
 currentItem.classList.remove("slider-list__item--active")
 // // 显示 指定 index 的图片
 nextItem.classList.add("slider-list__item--active")
 // 取消 当前圆点的高亮状态
 currentButton.classList.remove("slider-list__controller-button--active")
 // 添加 要展示圆点的高亮状态
 nextButton.classList.add("slider-list__controller-button--active")
 }

至此,一个简单的轮播图就实现完成了。

但是,当1个页面中有2个 轮播图的情况下。我们写的代码 很难复用。会造成的代码的重复。

还有, 当我们需要添加 除了 圆点控制器外的 其他控制器的话,就需要 更改 slideTo函数。会造成控制器与 图片轮播的代码强耦合。

那下一篇,我们来基于类 重新组织的我们的代码。使我们的轮播组件能够可复用和可扩展。

附:第一版本 完整代码:

HTML代码

<!DOCTYPE html>
<html lang="en">
<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>轮播组件开发</title>
    <link rel="stylesheet" href="./slider.css">
</head>
<body>
    <div id="my-slider" class="slider-list">
        <!-- 图片 -->
        <ul>
            <li class="slider-list__item slider-list__item--active">
                ![](./img/1.jpg)
            </li>
            <li class="slider-list__item">
                ![](./img/2.jpg)
            </li>
            <li class="slider-list__item">
                ![](./img/3.jpg)
            </li>
        </ul>
        
        <!--小圆点-->
        <div class="slider-list__controller">
            <span class="slider-list__controller-button slider-list__controller-button--active"></span>
            <span class="slider-list__controller-button"></span>
            <span class="slider-list__controller-button"></span>
        </div>
        <!--上一张-->
        <a href="" class="slide-list__previous"></a>
        <!--下一张-->
        <a href="" class="slide-list__next"></a>
    </div>


    <script src="./slider1.js"></script>
    
</body>
</html>```
<!DOCTYPE html>
<html lang="en">
<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>轮播组件开发</title>
 <link rel="stylesheet" href="./slider.css">
</head>
<body>
 <div id="my-slider" class="slider-list">
 <!-- 图片 -->
 <ul>
 <li class="slider-list__item slider-list__item--active">
 <img src="./img/1.jpg" alt="" class = "slider-list__img">
 </li>
 <li class="slider-list__item">
 <img src="./img/2.jpg" alt="" class ="slider-list__img">
 </li>
 <li class="slider-list__item">
 <img src="./img/3.jpg" alt="" class = "slider-list__img">
 </li>
 </ul>
 
 <!--小圆点-->
 <div class="slider-list__controller">
 <span class="slider-list__controller-button slider-list__controller-button--active"></span>
 <span class="slider-list__controller-button"></span>
 <span class="slider-list__controller-button"></span>
 </div>
 <!--上一张-->
 <a href="" class="slide-list__previous"></a>
 <!--下一张-->
 <a href="" class="slide-list__next"></a>
 </div>
 <script src="./slider1.js"></script>
 
</body>
</html>

CSS代码

* {
 margin:0;
 padding:0
 }
 
 ul{
 list-style:none
 }
 #my-slider {
 width: 600px;
 height:400px
 }
 /* 图片列表 */
 .slider-list{
 position:relative;
 
 }
 .slider-list ul{
 height:100%
 }
 .slider-list__item {
 position:absolute;
 opacity: 0;
 height:100%;
 
 }
 .slider-list__img {
 width:100%;
 height:100%;
 }
 .slider-list__item--active {
 transition: opacity 1s;
 opacity: 1;
 }
 /* 上一张 下一张 样式 */
 .slide-list__next,
 .slide-list__previous {
 position: absolute;
 top: 50%;
 width: 30px;
 height: 35px;
 background: rgba(0, 0, 0, 0.5);
 transform: translateY(-50%);
 color: white;
 text-align: center;
 line-height: 35px;
 text-decoration: none;
 }
 
 .slide-list__next {
 right: 0;
 }
 
 .slide-list__previous::before {
 content: "<";
 }
 
 .slide-list__next::before {
 content: ">";
 }
.slide-list__next::before {
 content: ">";
}
/* 新增小圆点样式 */
.slider-list__controller {
 position: absolute;
 left: 20px;
 bottom: 30px;
}
.slider-list__controller-button {
 display: inline-block;
 width: 10px;
 height: 10px;
 background: rgba(255, 255, 255,.4);
 border-radius: 50%;
}
.slider-list__controller-button--active {
 background: orange;
}

js代码

document.addEventListener("DOMContentLoaded",(e) => {
 // 获取轮播容器
 const container = document.getElementById("my-slider");
 // 获取轮播的图片集合
 const items = container.querySelectorAll(".slider-list__item");
 // 定时器变量
 // let timer = null;
 // 多长时间轮播一张图片
 const interval = 5000;
 // 获取上一张按钮
 const previousButton =  container.querySelector(".slide-list__previous")
 // 获取下一张按钮
 const nextButton =  container.querySelector(".slide-list__next") 
 //获取 小圆点 controller  容器
 const controller = container.querySelector(".slider-list__controller");
 // 获取 小圆点集合
 const buttons = controller.querySelectorAll(".slider-list__controller-button");
 // 给上一张按钮绑定事件
 previousButton.addEventListener("click",(e)=>{
 stop()
 previous()
 autoStart()
 e.preventDefault()
 })
 // 给下一张按钮绑定事件
 nextButton.addEventListener("click",(e)=>{
 stop()
 next()
 autoStart()
 e.preventDefault()
 })
 // 鼠标经过事件停止自动轮播, 并切换到当前位置
 controller.addEventListener("mouseover",(e)=>{
 const nextItemIndex = getControllerIndex(e.target)
 // 如果存在
 if(nextItemIndex > -1){
 // 停止轮播
 stop()
 slideTo(nextItemIndex)
 }
 })
 // 鼠标离开时候启动 自动轮播
 controller.addEventListener("mouseout",(e)=>{
 const nextItemIndex = getControllerIndex(e.target)
 //如果存在
 if(nextItemIndex > -1){
 // 自动播放
 autoStart()
 }
 })
 // 获取当前的图片dom节点
 function getCurrentItem(){
 return container.querySelector(".slider-list__item--active")
 }
 // 获取当前的图片位置
 function getCurrentItemIndex(){
 return Array.from(items).indexOf(getCurrentItem())
 }
 /**
 * 获取控制点的位置
 * @param  {String} target  鼠标经过的节点
 * @return {Number}  鼠标经过节点的位置
 */
 function getControllerIndex(target){
 const nextItemIndex = Array.from(buttons).indexOf(target);
 return nextItemIndex
 }
 //下一张图片
 function next() {
 // 获取下一张图片的位置
 const nextItemIndex = ( getCurrentItemIndex() + 1) % items.length;
 // 切换
 slideTo(nextItemIndex)
 }
 /**
 * 切换到指定位置的图片
 * @param  {number} index 图片的位置
 * @return {undefined} 
 */
 function slideTo( index ) {
 // 获取当前展示的图片 节点
 const currentItem = getCurrentItem(),
 // 获取下一次要展示的图片节点
 nextItem = items[index],
 // 获取 当前展示 圆点的 节点
 currentButton = buttons[getCurrentItemIndex()],
 // 获取 下一次要展示的圆点节点
 nextButton = buttons[index]
 
 
 // 隐藏 当前的图片
 currentItem.classList.remove("slider-list__item--active")
 // // 显示 指定 index 的图片
 nextItem.classList.add("slider-list__item--active")
 
 
 
 // 取消 当前圆点的高亮状态
 currentButton.classList.remove("slider-list__controller-button--active")
 // 添加 要展示圆点的高亮状态
 nextButton.classList.add("slider-list__controller-button--active")
 
 
 }
 //自动轮播
 function autoStart() {
 let timer = setInterval(function(){ 
 // 切换到下一张
 next()
 },interval)
 }
 //开始轮播
 autoStart()
 
 });

web前端老孟
0 声望1 粉丝