JavaScript实战-王者荣耀轮播图

1,388 阅读4分钟

王者荣耀轮播图

image.png

1. 布局+样式

<!DOCTYPE html>
<html lang="zh-CN">
<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="./css/reset.css">
  <style>
    .news-section {
      margin: 20px auto;
      display: flex;
      justify-content: center;
      height: 342px;
    }

    .news-section .banner {
      width: 605px;
      background-color: #000;
      overflow: hidden;
    }

    .news-section .banner .image-list {
      display: flex;
      width: 604px;
    }

    .news-section .banner .image-list .item {
      flex-shrink: 0;
      width: 100%;
    }

    .news-section .banner .image-list .item a {
      display: block;
    }

    .news-section .banner .image-list .item a img {
      width: 100%;
    }

    .news-section .banner .title-list {
      display: flex;
      height: 44px;
      line-height: 44px;
    }

    .news-section .banner .title-list .item {
      flex: 1;
      text-align: center;
    }

    .news-section .banner .title-list .item a {
      display: block;
      font-size: 14px;
      color: #b1b2be;
    }
    
    // hover后效果
    .news-section .banner .title-list .item.active a,
    .news-section .banner .title-list .item a:hover {
      color: #f3c258;
      background-color: rgba(255,255,255,.15);
    }

  </style>
</head>
<body>
  
  <div class="news-section">
    <div class="banner">
      <ul class="image-list">
        <li class="item">
          <a href="">
            <img src="./img/banner_01.jpeg" alt="">
          </a>
        </li>
        <li class="item">
          <a href="">
            <img src="./img/banner_02.jpeg" alt="">
          </a>
        </li>
        <li class="item">
          <a href="">
            <img src="./img/banner_03.jpeg" alt="">
          </a>
        </li>
        <li class="item">
          <a href="">
            <img src="./img/banner_04.jpeg" alt="">
          </a>
        </li>
        <li class="item">
          <a href="">
            <img src="./img/banner_05.jpeg" alt="">
          </a>
        </li>
      </ul>
      <ul class="title-list">
        <li class="item active">
          <a href="#">桑启的旅途故事</a>
        </li>
        <li class="item">
          <a href="#">启示之音抢先听</a>
        </li>
        <li class="item">
          <a href="#">谁成为版本之子</a>
        </li>
        <li class="item">
          <a href="#">观赛体验升级</a>
        </li>
        <li class="item">
          <a href="#">季后赛开战</a>
        </li>
      </ul>
    </div>
  </div>
</body>
</html>

2. 鼠标移入标题,图片相应切换

  • 鼠标移入标题,当前作用的元素(<li class="item">)变为活跃状态,而其他的元素(<li class="item">)取消活跃状态;

  • 标题活跃状态切换时,图片也随之变化,细心可以发现,图片与标题其实是一一对应的关系,所以只需要找到当前活跃状态的标题的索引,去修改展示图片的位置就可以,即让图片进行平移translateX(-${index * 604}px)

  • 添加渐变动画效果 transition

    // 1.获取元素
    // 标题元素ul
    var titleListEl = document.querySelector(".title-list")
    // 当前选中的标题
    var activeItemEl = document.querySelector(".active")
    // 图片元素ul
    var imageListEl = document.querySelector(".image-list")
    
    // 2. 底部titles的切换
    titleListEl.onmouseover = function(event) {
      // 因为 active 写在li上,此处的event.target作用在a元素上
      var itemEl = event.target.parentElement
    
      if (!itemEl.classList.contains('item')) return
    
      // 对已有 active 的元素移除掉
      activeItemEl.classList.remove('active')
      // 当前作用的元素添加 active
      itemEl.classList.add('active')
    
      // 当前元素为 active
      activeItemEl = itemEl
    
      var index = Array.from(titleListEl.children).findIndex(item => item === itemEl)
    
      imageListEl.style.transform = `translateX(-${index * 604}px)`
      imageListEl.style.transition = 'all 300ms ease'
    }
    

3. 图片自动轮播

  • 定义一个全局变量curIndex,记录当前图片的索引,每3s钟,索引加一,就可以实现图片的切换;同时标题也应该随curIndex的切换而切换。

  • 当索引等于标题子元素的长度时,将其恢复为0,即第一个图片的索引

    // 1.获取元素
    var titleListEl = document.querySelector(".title-list")
    var imageListEl = document.querySelector(".image-list")
    
    // 定义全局变量
    var activeItemEl = document.querySelector(".active")
    var curIndex = 0
    
    // 2.底部titles的切换, 同时进行轮播
    titleListEl.onmouseover = function(event) {
      // 因为 active 写在li上,此处的event.target作用在a元素上
      var itemEl = event.target.parentElement
    
      if (!itemEl.classList.contains('item')) return
    
      // 对已有 active 的元素移除掉
      activeItemEl.classList.remove('active')
      // 当前作用的元素添加 active
      itemEl.classList.add('active')
    
      //当前元素为 active
      activeItemEl = itemEl
    
      var index = Array.from(titleListEl.children).findIndex(item => item === itemEl)
    
      imageListEl.style.transform = `translateX(-${index * 604}px)`
      imageListEl.style.transition = 'all 300ms ease'
    }
    
    // 自动轮播
    setInterval(() => {
    
      curIndex++
    
      if(curIndex === imageListEl.children.length) {
        curIndex = 0
      }
      
      // 处理图片
      imageListEl.style.transform = `translateX(-${curIndex * 604}px)`
      imageListEl.style.transition = 'all 300ms ease'
    
      // 处理titles中的li的active
      // 1.移除之前的 active
      activeItemEl.classList.remove('active')
    
      // 2.添加新的 active
      var itemEl = titleListEl.children[curIndex]
      itemEl.classList.add('active')
    
      // 3.记录新的 activeLi
      activeItemEl = itemEl
    
    }, 3000)
    

4. 代码重构-封装切换函数

  • 细心的同学应该发现了在鼠标移入标题的时候,图片会变换位置,标题的active也在变化,而自动轮播时,两者也都需要变化,区别仅仅是用到的index不同,但实际上鼠标移入标题时,curIndex就应该更新为当前元素的索引,即curIndex = index

  • 将相同的部分进行抽离成switchBanner函数,不仅提高复用性,而且减少了冗余代码

    // 1.获取元素
    var titleListEl = document.querySelector(".title-list")
    var imageListEl = document.querySelector(".image-list")
    
    // 定义全局变量
    var activeItemEl = document.querySelector(".active")
    var curIndex = 0
    
    // 2.底部titles的切换, 同时进行轮播
    titleListEl.onmouseover = function(event) {
      // 因为 active 写在li上,此处的event.target作用在a元素上
      var itemEl = event.target.parentElement
    
      if (!itemEl.classList.contains('item')) return
    
      // 获取对应的索引index
      var index = Array.from(titleListEl.children).findIndex(item => item === itemEl)
      curIndex = index
    
      // 调用切换函数
      switchBanner()
    }
    
    // 3.定时轮播
    setInterval(() => {
    
      curIndex++
    
      if(curIndex === imageListEl.children.length) {
        curIndex = 0
      }
    
      // 调用切换函数
      switchBanner()
    
    }, 3000)
    
    function switchBanner() {
      // 处理图片
      imageListEl.style.transform = `translateX(-${curIndex * 604}px)`
      imageListEl.style.transition = 'all 300ms ease'
    
      // 处理标题
      // 1.移除之前的 active
      activeItemEl.classList.remove('active')
    
      // 2.添加新的 active
      var itemEl = titleListEl.children[curIndex]
      itemEl.classList.add('active')
    
      // 3.记录新的 activeLi
      activeItemEl = itemEl
    }
    

5. 移除定时器

  • 可能有同学已经发现问题了,当我鼠标移入到某个标题不动时,过了3s,图片依然滚动了,当前移入的标题为active,而图片滚动,与之对应的标题也变成了active,这怎能行呢?

  • 所以当鼠标移入标题的时候应该清除定时器,而离开时重新计时,即重新轮播,那么如何重新轮播呢?

    • 需要执行一个新的定时器来开启轮播
    • 同时又需要移除这个定时器,所以可以定义一个全局变量timer来记录
  • 相对应,当鼠标在图片上呢,是不是也不应该轮播呢?当然,这个时候也需要移除定时器的

  • 注意:无论是图片还是标题,两者都需要在鼠标移入的时候移除定时器,移出的时候重新开始定时器,所以可以在其父元素上使用mouseentermouseleave方法。mouseovermouseenter的区别可参考我的另一篇文章JavaScript的事件详解

// 1.获取元素
var titleListEl = document.querySelector(".title-list")
var imageListEl = document.querySelector(".image-list")
var bannerEl = document.querySelector(".banner")

// 定义全局变量
var activeItemEl = document.querySelector(".active")
var curIndex = 0
var timer = null

// 2.底部titles的切换, 同时进行轮播
titleListEl.onmouseover = function(event) {
  // 因为 active 写在li上,此处的event.target作用在a元素上
  var itemEl = event.target.parentElement

  if (!itemEl.classList.contains('item')) return

  // 获取对应的索引index
  var index = Array.from(titleListEl.children).findIndex(item => item === itemEl)
  curIndex = index

  // 调用切换函数
  switchBanner()
}

// 监听banner的事件
bannerEl.onmouseenter = function () {
  clearInterval(timer)
}

bannerEl.onmouseleave = function () {
  startTimer()
}

// 3.定时轮播
startTimer()

// 封装切换轮播的函数
function switchBanner() {
  // 处理图片
  imageListEl.style.transform = `translateX(-${curIndex * 604}px)`
  imageListEl.style.transition = 'all 300ms ease'

  // 处理标题
  // 1.移除之前的 active
  activeItemEl.classList.remove('active')

  // 2.添加新的 active
  var itemEl = titleListEl.children[curIndex]
  itemEl.classList.add('active')

  // 3.记录新的 activeLi
  activeItemEl = itemEl
}

// 封装添加定时器的函数
function startTimer() {
  timer = setInterval(() => {
    curIndex++

    if(curIndex === imageListEl.children.length) {
      curIndex = 0
    }

    // 调用切换函数
    switchBanner()

  }, 3000)
}

6. 官网的效果

上面几个步骤已经实现了轮播,可能又有细心的同学发现官网上的效果与我们做的有点儿不太一样,官网上图片之间的切换并没有夹杂其他图片,下面就来实现官网的效果

  • 上面实现的轮播其实是把图片整体进行位移,每当index变化,就将图片进行偏移,而官网的实现是每当进行切换时,将图片仅移动一个位置,简单理解就是将索引值小于当前展示图片索引的放到图片左边堆叠一起,大的放到右边堆叠一起left: 100%,切换时将对应的图片进行移动, 既然堆叠就需要使用定位来实现,移动时改变图片的left值即可

  • 注意:加动画的时候,仅对当前的图片和上一个图片添加transition,而其他的图片不应该有动画,防止有冲突,定义一个全局变量prevIndex来记录上一张图片的索引

  • 总结:

    • 摆正每个imageItem的位置
    • 找到正确的imageItem
      • i === currentIndex
        • 设置left0
        • 并且添加动画
      • i < currentIndex
        • 设置left-100%
        • 清除动画
      • i > currentIndex
        • 设置left100%
        • 清除动画
    • 原来在active状态的元素不需要清除
      • previousIndex记录之前位置的元素
  • 核心代码如下:

    for (let i = 0; i < imageListEl.children.length; i++) {
        var curItem = imageListEl.children[i]
        if(i === curIndex) {
          curItem.style.transition = 'left 300ms ease'
          curItem.style.left = '0'
        } else if(i < curIndex) {
          // 如果当前的i等于上一张图片索引,则需要动画,反之移除动画
          if(i !== prevIndex) curItem.style.transition = 'none'
          curItem.style.left = '-100%'
        } else {
          if(i !== prevIndex) curItem.style.transition = 'none'
          curItem.style.left = '100%'
        }
      }
    
  • 完整代码如下:

<!DOCTYPE html>
<html lang="zh-CN">
<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="./css/reset.css">
  <style>
    .news-section {
      margin: 20px auto;
      display: flex;
      justify-content: center;
      height: 342px;
    }

    .news-section .banner {
      width: 605px;
      background-color: #000;
      overflow: hidden;
    }

    .news-section .banner .image-list {
      position: relative;
      display: flex;
      width: 604px;
      height: 298px;
    }

    .news-section .banner .image-list .item {
      position: absolute;
      left: 100%;
      flex-shrink: 0;
      width: 100%;
    }

    .news-section .banner .image-list .item:first-child {
      left: 0;
      transition: left 300ms ease;
    }

    .news-section .banner .image-list .item a {
      display: block;
    }

    .news-section .banner .image-list .item a img {
      width: 100%;
    }

    .news-section .banner .title-list {
      display: flex;
      height: 44px;
      line-height: 44px;
    }

    .news-section .banner .title-list .item {
      flex: 1;
      text-align: center;
    }

    .news-section .banner .title-list .item a {
      display: block;
      font-size: 14px;
      color: #b1b2be;
    }
    .news-section .banner .title-list .item.active a,
    .news-section .banner .title-list .item a:hover {
      color: #f3c258;
      background-color: rgba(255,255,255,.15);
    }

  </style>
</head>
<body>
  
  <div class="news-section">
    <div class="banner">
      <ul class="image-list">
        <li class="item">
          <a href="">
            <img src="./img/banner_01.jpeg" alt="">
          </a>
        </li>
        <li class="item">
          <a href="">
            <img src="./img/banner_02.jpeg" alt="">
          </a>
        </li>
        <li class="item">
          <a href="">
            <img src="./img/banner_03.jpeg" alt="">
          </a>
        </li>
        <li class="item">
          <a href="">
            <img src="./img/banner_04.jpeg" alt="">
          </a>
        </li>
        <li class="item">
          <a href="">
            <img src="./img/banner_05.jpeg" alt="">
          </a>
        </li>
      </ul>
      <ul class="title-list">
        <li class="item active">
          <a href="#">桑启的旅途故事</a>
        </li>
        <li class="item">
          <a href="#">启示之音抢先听</a>
        </li>
        <li class="item">
          <a href="#">谁成为版本之子</a>
        </li>
        <li class="item">
          <a href="#">观赛体验升级</a>
        </li>
        <li class="item">
          <a href="#">季后赛开战</a>
        </li>
      </ul>
    </div>
  </div>

  <script>
    // 1.获取元素
    var titleListEl = document.querySelector(".title-list")
    var imageListEl = document.querySelector(".image-list")
    var bannerEl = document.querySelector(".banner")

    // 定义全局变量
    var activeTitleEl = document.querySelector(".active")
    var curIndex = 0
    var prevIndex = 0
    var timer = null

    // 2.底部titles的切换, 同时进行轮播
    titleListEl.onmouseover = function(event) {
      // 因为 active 写在li上,此处的event.target作用在a元素上
      var itemEl = event.target.parentElement

      if (!itemEl.classList.contains('item')) return

      // 获取对应的索引index
      var index = Array.from(titleListEl.children).findIndex(item => item === itemEl)
      prevIndex = curIndex
      curIndex = index

      // 调用切换函数
      switchBanner()
    }

    // 监听banner的事件
    bannerEl.onmouseenter = function () {
      clearInterval(timer)
    }

    bannerEl.onmouseleave = function () {
      startTimer()
    }

    // 3.定时轮播
    startTimer()


    // 封装切换轮播的函数
    function switchBanner() {
      // 处理图片
      for (let i = 0; i < imageListEl.children.length; i++) {
        var curItem = imageListEl.children[i]
        if(i === curIndex) {
          curItem.style.transition = 'left 300ms ease'
          curItem.style.left = '0'
        } else if(i < curIndex) {
          if(i !== prevIndex) curItem.style.transition = 'none'
          curItem.style.left = '-100%'
        } else {
          if(i !== prevIndex) curItem.style.transition = 'none'
          curItem.style.left = '100%'
        }
      }

      // 处理标题
      // 1.移除之前的 active
      activeTitleEl.classList.remove('active')

      // 2.添加新的 active
      var itemEl = titleListEl.children[curIndex]
      itemEl.classList.add('active')

      // 3.记录新的 activeLi
      activeTitleEl = itemEl
    }

    // 封装添加定时器的函数
    function startTimer() {
      timer = setInterval(() => {
        prevIndex = curIndex
        curIndex++

        if(curIndex === imageListEl.children.length) {
          curIndex = 0
        }

        // 调用切换函数
        switchBanner()

      }, 3000)
    }

  </script>
</body>
</html>

收官!!!

传送门:无限轮播效果在这篇文章里哦!JavaScript实战-华为商城轮播图