JS实现轮播图效果

584 阅读4分钟

    思路

  1. 获取元素
  2. 设置鼠标图片区域时显示左右按钮
  3. 为了实现无缝滚动—克隆第一张图片的li放在最后
  4. 有几张轮播图就动态生成几个底部小圆圈
  5. 鼠标点击底部小按钮 点击的那个会变色
  6. 点击小圆点 跳转图片ul
  7. 点击右侧按钮,底部按钮跟右侧一起变化
  • 绑定底部小按钮 跟随轮播图一起变化
  • 轮播图片限定范围 限定右边 实现无缝滚动效果
  • 轮播到第5张图的时候 底部按钮点亮第1个
  • 快速点击 图片会快速轮播 需要节流阀限制快速轮播
  1. 自动播放轮播图
  • 自动播放轮播图 鼠标经过 就停止
  • 自动播放轮播图 鼠标移出 就开始

山海有灵艺术插画来自于站酷文件已开源至GitHub


实现效果:

Video_2022-01-30_200449_1_1.gif


    动画函数

function animate(obj, target, callback) {
  // console.log(callback);  callback = function() {}  调用的时候 callback()

  // 先清除以前的定时器,只保留当前的一个定时器执行
  clearInterval(obj.timer)
  obj.timer = setInterval(function () {
    // 步长值写到定时器的里面
    // 把我们步长值改为整数 不要出现小数的问题
    // var step = Math.ceil((target - obj.offsetLeft) / 10);
    var step = (target - obj.offsetLeft) / 23
    step = step > 0 ? Math.ceil(step) : Math.floor(step)
    if (obj.offsetLeft == target) {
      // 停止动画 本质是停止定时器
      clearInterval(obj.timer)
      // 回调函数写到定时器结束里面
      // if (callback) {
      //     // 调用函数
      //     callback();
      // }
      callback && callback()
    }
    // 把每次加1 这个步长值改为一个慢慢变小的值  步长公式:(目标值 - 现在的位置) / 10
    obj.style.left = obj.offsetLeft + step + 'px'
  }, 5)
}

    轮播图JS

window.onload = function () {
  // # 1. 获取元素
  let buttonL = document.querySelector('.iconl') //获取左按钮
  let buttonR = document.querySelector('.iconr') //获取右按钮
  let img_left = document.querySelector('.img-left') //获取整个左边区域
  let viewpager = img_left.querySelector('.viewpager') //获取所有轮播图ul
  let img = viewpager.querySelector('img') //获取每张轮播图
  let B_button = document.querySelector('.bottom-button').querySelector('ul') //获取所有底部小按钮ul
  let index = 0 //给index一个初始值
  let imgwidth = img.offsetWidth //每张轮播图片的宽度
  // # 2. 鼠标经过 就显示/隐藏左右按钮
  //鼠标经过
  img_left.addEventListener('mouseenter', function () {
    buttonL.style.opacity = 1
    buttonR.style.opacity = 1 //显示
    // # 8.1 自动播放轮播图 优化:鼠标经过 就停止
    clearInterval(time)
    time = null // 清除定时器变量
  })
  //鼠标离开
  img_left.addEventListener('mouseleave', function () {
    buttonL.style.opacity = 0
    buttonR.style.opacity = 0 //隐藏
    // # 8.2 自动播放轮播图 优化:鼠标移出 就开始
    time = setInterval(function () {
      //手动调用点击事件1
      buttonR.click()
    }, 2000)
  })

  // # 3. 为了实现无缝滚动-克隆第一张图片的li放到最后
  let clonefirst = viewpager.children[0].cloneNode(true) //深拷贝第一张轮播图
  viewpager.appendChild(clonefirst) //向节点添加最后一个子节点

  // # 4. 有几张轮播图就动态生成几个底部小圆圈
  //遍历轮播图的ul
  for (let i = 0; i < viewpager.children.length - 1; i++) {
    let li = document.createElement('li') // 创建一个li
    li.setAttribute('index', i) // 将li创建index属性并赋值
    B_button.appendChild(li) // 把小li插入到ul里面

    // # 5. 鼠标点击底部小按钮 点击的那个会变色
    B_button.children[0].className = 'first' //给底部按钮的第一张图片样式
    li.addEventListener('click', function () {
      //遍历底部按钮ul 清空样式
      for (let i = 0; i < B_button.children.length; i++) {
        B_button.children[i].className = ''
      }
      //点击后 给样式
      li.className = 'current'

      // # 6. 点击小圆点 跳转图片ul
      // ul的移动距离 = 小圆圈的索引号 * 图片的宽度
      // let imgwidth = img.offsetWidth //每张轮播图片的宽度
      index = this.getAttribute('index') //点击谁就得到当前的索引号
      animate(viewpager, -index * imgwidth)
    })
  }

  // # 7.4 优化3:快速点击 图片会快速轮播 需要节流阀限制快速轮播
  let flag = true // 设置一个变量 flag 节流阀
  // # 7. 点击右侧按钮,底部按钮跟右侧一起变化
  buttonR.addEventListener('click', function () {
    // # 7.2 优化1:轮播图片限定范围 限定右边 实现无缝滚动效果
    if (flag) {
      //点击一次后关闭
      flag = false

      index == viewpager.children.length - 1
        ? (viewpager.style.left = 0)
        : index
      index == viewpager.children.length - 1 ? (index = 0) : index
      index++

      //点击小按钮 让索引号自加

      console.log(index)
      console.log(-index * imgwidth)
      animate(viewpager, -index * imgwidth, function () {
        flag = true // 利用回调函数 动画执行完毕后 赋值为true,可以执行if里面的运算
      })

      // # 7.1 绑定底部小按钮 跟随轮播图一起变化
      for (let i = 0; i < B_button.children.length; i++) {
        B_button.children[i].className = ''
      }
      // # 7.3 优化2:轮播到第5张图的时候 底部按钮点亮第1个
      let circle = index
      circle == viewpager.children.length - 1 ? (circle = 0) : circle
      B_button.children[circle].className = 'current'
    }
  })
  buttonL.addEventListener('click', function () {
    if (flag) {
      flag = false
      console.log(-index * imgwidth)
      // index == 0 ? (viewpager.style.left = -4580 + 'px') : index
      if (index == 0) {
        index = viewpager.children.length - 1
        viewpager.style.left = -index * imgwidth + 'px'
      }
      index == 0 ? (index = viewpager.children.length - 1) : index

      index--
      animate(viewpager, -index * imgwidth, function () {
        flag = true
      })
      for (let i = 0; i < B_button.children.length; i++) {
        B_button.children[i].className = ''
      }
      let circle = index
      circle == viewpager.children.length - 1 ? (circle = 0) : circle
      B_button.children[circle].className = 'current'
    }
  })

  // # 8. 自动播放轮播图
  let time = setInterval(function () {
    //手动调用点击事件
    buttonR.click()
  }, 2500)
}


    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="./css/base.css" />
    <link rel="stylesheet" href="./iconfont/iconfont.css" />
    <link rel="stylesheet" href="./css/viewpager.css" />
    <script src="./js/animate.js"></script>
    <script src="./js/index.js"></script>
  </head>
  <!-- !!添加效果点击更换背景 -->
  <body>
    <div class="bigbox w">
      <!-- !!左边轮播图 -->
      <div class="img-left">
        <ul class="viewpager">
          <li>
            <a href="#">
              <img src="./img/《山海有灵》合集_XISION-站酷ZCOOL_1.jpg" alt=""
            /></a>
          </li>
          <li>
            <a href="#">
              <img src="./img/《山海有灵》合集_XISION-站酷ZCOOL_2.jpg" alt=""
            /></a>
          </li>
          <li>
            <a href="#">
              <img src="./img/《山海有灵》合集_XISION-站酷ZCOOL_3.jpg" alt=""
            /></a>
          </li>
          <li>
            <a href="#">
              <img src="./img/《山海有灵》合集_XISION-站酷ZCOOL_4.jpg" alt=""
            /></a>
          </li>
          <li>
            <a href="#">
              <img src="./img/《山海有灵》合集_XISION-站酷ZCOOL_5.jpg" alt=""
            /></a>
          </li>
        </ul>
        <!-- #左右按钮 -->
        <div class="left-right-button">
          <a href="javascript:;">
            <i class="iconfont icon-xialajiantouxiao iconl"></i
          ></a>
          <a href="javascript:;">
            <i class="iconfont icon-xialajiantouxiao iconr"></i>
          </a>
        </div>
        <!-- #底部按钮 -->
        <div class="bottom-button">
          <ul>
            <!--       <li></li>
            <li></li>
            <li></li>
            <li></li>
            <li></li> -->
          </ul>
        </div>
      </div>
      <!-- !!右边文字 -->
      <div class="text-right">
        <h3>山海有灵</h3>
        <p class="world">
          “晴对雨,地对天,天地对山川”<br />
          今年,他的毕业设计在网络刷屏<br />
          龙、梯田与劳动人民的交错令人赞叹不已<br />
          凝视着画中独具神韵的青龙<br />
          耳边仿佛响起了远古的龙吟<br />
          "龙作为画面主题不但为画面的故事增添了更多神秘色彩<br />
          也增添了画面的视觉冲击力<br />
          在中国文化中,龙有着呼风唤雨的本事<br />
          龙的出现则代表了耕耘丰收<br />
          所以整套作品是对中国农耕文化的致敬与再活化。"<br />
        </p>
        <p class="writer">——xision</p>
      </div>
    </div>
  </body>
</html>


    less部分

.w {
  margin: 220px auto;
  width: 1360px;
}
body {
  .icon-jiantoushang {
    color: aliceblue;
  }
  background-color: rgb(26, 26, 26);
  .bigbox {
    background-color: rgb(83, 83, 83);
    display: flex;
    align-items: center;
    justify-content: space-between;
    height: 380px;
    box-shadow: 1px 1px 6px rgba(255, 255, 255, 0.3);
    // 左边轮播图
    .img-left {
      position: relative;
      left: 10px;
      width: 916px;
      height: 360px;
      overflow: hidden;
      .viewpager {
        position: absolute;
        top: 0;
        left: 0;
        display: flex;
        img {
          width: 916px;
          height: 360px;
        }
      }
      // #左右按钮
      .left-right-button {
        .iconl {
          position: absolute;
          bottom: 156px;
          left: 5px;
          font-size: 30px;
          color: white;
          transform: rotate(90deg);
        }
        .iconr {
          position: absolute;
          bottom: 156px;
          right: 5px;
          font-size: 30px;
          color: white;
          transform: rotate(-90deg);
        }

        .icon-xialajiantouxiao:active {
          color: rgb(155, 217, 255);
        }

        .iconfont {
          cursor: pointer;
          transition: all 0.3s;
        }
      }
      // #底部按钮
      .bottom-button {
        ul {
          position: absolute;
          width: 150px;
          height: 20px;
          // background-color: antiquewhite;
          bottom: 0px;
          left: 50%;
          transform: translate(-50%, -50%);
          display: flex;
          justify-content: space-around;
          align-items: center;
          li {
            width: 12px;
            height: 12px;
            cursor: pointer;
            border-radius: 50%;
            background-color: #fff;
            outline: 1.5px solid #2980b9;
          }
          .first {
            width: 12px;
            height: 12px;
            cursor: pointer;
            border-radius: 50%;
            background-color: #a9e8ff;
            outline: 1.5px solid #2980b9;
          }
          .current {
            width: 12px;
            height: 12px;
            cursor: pointer;
            border-radius: 50%;
            background-color: #a9e8ff;
            outline: 1.5px solid #2980b9;
          }
        }
      }
    }
  }
  //右边文字
  .text-right {
    display: flex;
    flex-direction: column;
    justify-content: center;
    h3 {
      padding-bottom: 10px;
      margin-left: 6px;
      color: white;
      font-size: 26px;
      // 字间距
      letter-spacing: 15px;
    }
    p {
      width: 370px;
      color: rgb(233, 233, 233);
    }
    .world {
      margin-top: 12px;
      line-height: 25px;
      font-size: 13px;
    }
    .writer {
      text-align: right;
      margin-top: 10px;
      padding-right: 25px;
      width: 340px;
    }
  }
}