纯 CSS,原生 JavaScript,jQuery,Vue,React 实现轮播图, 一篇文章帮你全部搞定

在这里插入图片描述


轮播图在平常的网页开发中十分常见,特别是电商类的网站。

前端 “进化” 到今天,组件化的思想早已深入人心。

组件库的出现更是提高了不少的开发效率。网上对于程序员是否需要重新造轮子一直还是有争论的。

但不管争论如何,该学的东西还是得学。毕竟面试需要,工资重要啊。

在这里插入图片描述
在这里插入图片描述

可以说只要是个组件库,就会有轮播图组件,也有单独做 swpier 的插件。你当然可以选择直接现成的轮子,也可以自己造一个(生活已经很累了,你为什么还要给我增加负担
在这里插入图片描述


纯 CSS

卧槽,CSS 这么牛逼的吗?o( ̄▽ ̄)d,CSS yyds 👍👍👍👍

基础版

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="utf-8">
  <title>轮播图</title>
  <style>
    * {
      margin: 0;
      padding: 0;
      text-align: none;
    }

    /*设置显示图片的大小,溢出隐藏*/
    .slidershow {
      width: 800px;
      height: 600px;
      overflow: hidden;
    }

    /*居中*/
    .middle {
      position: absolute;
      top: 50%;
      left: 50%;
      transform: translate(-50%, -50%);
      /*实现块元素百分比下居中*/
    }

    /*导航 设置绝对位置,弹性布局,按钮位置*/
    .navigaion {
      position: absolute;
      bottom: 5px;
      /*底部距离*/
      left: 50%;
      transform: translateX(-50%);
      display: flex;
    }

    /*设置按钮属性*/
    .bar {
      width: 50px;
      height: 10px;
      border: 2px solid #FFF8DC;
      margin: 0.375rem;
      cursor: pointer;
      transition: 0.4s;
    }

    .bar:hover {
      background-color: #FFF8DC;
    }

    /*隐藏了按钮*/
    input[name="r"] {
      position: absolute;
      visibility: hidden;
    }

    /* 图片部分 */
    .slides {
      width: 1000%;
      height: 100%;
      display: flex;
    }

    .slide {
      width: 10%;
      transition: 1.5s;
    }

    .slide img {
      width: 100%;
      height: 100%;
    }

    /* 绑定按钮 */
    /* radio 标签与 img 相关联 */
    #r1:checked~.s1 {
      margin-left: 0;
    }

    #r2:checked~.s1 {
      margin-left: -10%;
    }

    #r3:checked~.s1 {
      margin-left: -20%;
    }

    #r4:checked~.s1 {
      margin-left: -30%;
    }
  </style>

</head>

<body>
  <div class="slidershow middle">
    <div class="slides">
      <input type="radio" name="r" id="r1" checked/>
      <input type="radio" name="r" id="r2" />
      <input type="radio" name="r" id="r3" />
      <input type="radio" name="r" id="r4" />

      <div class="slide s1">
        <img src="https://cdn.pixabay.com/photo/2020/09/20/10/08/mountain-5586606__340.jpg" alt="photo" />
      </div>

      <div class="slide">
        <img src="https://cdn.pixabay.com/photo/2020/09/11/17/01/landscape-5563684__340.jpg" alt="photo" />
      </div>

      <div class="slide">
        <img src="https://cdn.pixabay.com/photo/2020/09/23/15/10/street-5596262__340.jpg" alt="photo" />
      </div>

      <div class="slide">
        <img src="https://cdn.pixabay.com/photo/2020/09/24/18/00/man-5599377__340.jpg" alt="photo" />
      </div>
    </div>

    <div class="navigaion">
      <label for="r1" class="bar"></label>
      <!-- label标签为 input 元素定义标注 "for"属性可把label绑定到另外一个元素。请把 "for" 属性的值设置为相关元素的 id 属性的值。 -->
      <label for="r2" class="bar"></label>
      <label for="r3" class="bar"></label>
      <label for="r4" class="bar"></label>
    </div>
  </div>
</body>
</html>

在这里插入图片描述

进阶版

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <title></title>
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  <meta http-equiv="X-UA-Compatible" content="ie=edge" />
  <style>
    * {
      padding: 0;
      margin: 0;
      box-sizing: border-box;
    }

    ul {
      list-style: none;
    }

    img {
      display: block;
      max-width: 100%;
      height: auto;
      background-color: #ededed;
    }

    label {
      display: block;
      cursor: pointer;
    }

    a {
      text-decoration: none;
      color: inherit;
    }

    /* 将 radio 按钮移除至屏幕外 */
    input[type="radio"] {
      position: absolute;
      bottom: 0;
      left: -9999px;
    }

    body {
      color: #fff;
      margin: 20px 0;
      font-family: sans-serif;
    }

    .container {
      max-width: 450px;
      padding: 0 20px;
      margin: 0 auto;
    }


    .slidershow-wrapper {
      position: relative;
    }

    /* 采用网格布局 */
    .slidershow-wrapper .slidershow-list {
      display: grid;
    }

    .slidershow-wrapper .slidershow-list li {
      grid-column: 1;
      grid-row: 1;
      opacity: 0;
      transition: opacity 0.25s;
    }

    /* 箭头样式 */
    .slidershow-wrapper .arrows label::before,
    .slidershow-wrapper .arrows label::after {
      position: absolute;
      top: 50%;
      transform: translateY(-50%);
      width: 40px;
      height: 40px;
      border-radius: 50%;
      color: #000;
      background-position: center;
      background-repeat: no-repeat;
      background-size: 24px 24px;
      background-color: #fff;
      opacity: 0.5;
      transition: opacity 0.25s;
    }

    .slidershow-wrapper .arrows label::before {
      left: 10px;
    }

    .slidershow-wrapper .arrows label::after {
      right: 10px;
    }

    /* 设置小圆圈 */
    .slidershow-wrapper .dots {
      position: absolute;
      bottom: 10px;
      left: 50%;
      transform: translateX(-50%);
      display: flex;
    }

    .slidershow-wrapper .dots li:not(:last-child) {
      margin-right: 8px;
    }

    .slidershow-wrapper .dots label {
      display: inline-block;
      width: 12px;
      height: 12px;
      border-radius: 50%;
      border: 1px solid #fff;
      transition: background 0.25s;
    }

    .slidershow-wrapper .dots label:hover {
      background: currentColor;
    }

    /* 设置小圆圈 */

    /* 缩略图布局 */
    .thumb-list {
      display: grid;
      grid-template-columns: repeat(3, 1fr);
      grid-column-gap: 20px;
      margin-top: 20px;
    }

    .thumb-list label {
      display: grid;
    }

    .thumb-list img,
    .thumb-list .outer {
      grid-column: 1;
      grid-row: 1;
    }

    .thumb-list .outer {
      display: grid;
      place-items: center;
      transition: background 0.25s;
    }

    .thumb-list .inner {
      font-size: 18px;
      opacity: 0;
      transform: translateY(20px);
      transition: all 0.25s;
    }

    /* 缩略图布局 */

    [id="image1"]:checked~.container .slidershow-list li:nth-child(1),
    [id="image2"]:checked~.container .slidershow-list li:nth-child(2),
    [id="image3"]:checked~.container .slidershow-list li:nth-child(3),
    [id^="image"]:checked~.container .arrows [for^="image"]:hover::before,
    [id^="image"]:checked~.container .arrows [for^="image"]:hover::after {
      opacity: 1;
    }

    [id="image1"]:checked~.container .arrows [for="image3"]::before,
    [id="image2"]:checked~.container .arrows [for="image1"]::before,
    [id="image3"]:checked~.container .arrows [for="image2"]::before {
      content: '';
      background-image: url(https://s3-us-west-2.amazonaws.com/s.cdpn.io/162656/arrow-prev-slideshow.svg);
    }

    [id="image1"]:checked~.container .arrows [for="image2"]::after,
    [id="image2"]:checked~.container .arrows [for="image3"]::after,
    [id="image3"]:checked~.container .arrows [for="image1"]::after {
      content: '';
      background-image: url(https://s3-us-west-2.amazonaws.com/s.cdpn.io/162656/arrow-next-slideshow.svg);
    }

    [id="image1"]:checked~.container .dots [for="image1"],
    [id="image2"]:checked~.container .dots [for="image2"],
    [id="image3"]:checked~.container .dots [for="image3"] {
      background: currentColor;
    }

    [id="image1"]:checked~.container [for="image1"] .outer,
    [id="image2"]:checked~.container [for="image2"] .outer,
    [id="image3"]:checked~.container [for="image3"] .outer {
      background: rgba(0, 0, 0, 0.75);
    }

    [id="image1"]:checked~.container [for="image1"] .inner,
    [id="image2"]:checked~.container [for="image2"] .inner,
    [id="image3"]:checked~.container [for="image3"] .inner {
      opacity: 1;
      transform: none;
    }
  </style>
</head>

<body>

  <!-- radio 标签 -->
  <input type="radio" id="image1" name="image" checked>
  <input type="radio" id="image2" name="image">
  <input type="radio" id="image3" name="image">

  <div class="container">
    <div class="slidershow-wrapper">
      <ul class="slidershow-list">
        <li>
          <figure>
            <img src="https://cdn.pixabay.com/photo/2020/09/20/10/08/mountain-5586606__340.jpg" alt="">
          </figure>
        </li>
        <li>
          <figure>
            <img src="https://cdn.pixabay.com/photo/2020/09/11/17/01/landscape-5563684__340.jpg" alt="">
          </figure>
        </li>
        <li>
          <figure>
            <img src="https://cdn.pixabay.com/photo/2020/09/23/15/10/street-5596262__340.jpg" alt="">
          </figure>
        </li>
      </ul>
      <ul class="arrows">
        <li>
          <label for="image1"></label>
        </li>
        <li>
          <label for="image2"></label>
        </li>
        <li>
          <label for="image3"></label>
        </li>
      </ul>
      <ul class="dots">
        <li>
          <label for="image1"></label>
        </li>
        <li>
          <label for="image2"></label>
        </li>
        <li>
          <label for="image3"></label>
        </li>
      </ul>
    </div>
    <ul class="thumb-list">
      <li>
        <label for="image1">
          <img src="https://cdn.pixabay.com/photo/2020/09/20/10/08/mountain-5586606__340.jpg" alt="">
          <span class="outer">
            <span class="inner"> Caption1 </span>
          </span>
        </label>
      </li>
      <li>
        <label for="image2">
          <img src="https://cdn.pixabay.com/photo/2020/09/11/17/01/landscape-5563684__340.jpg" alt="">
          <span class="outer">
            <span class="inner"> Caption2 </span>
          </span>
        </label>
      </li>
      <li>
        <label for="image3">
          <img src="https://cdn.pixabay.com/photo/2020/09/23/15/10/street-5596262__340.jpg" alt="">
          <span class="outer">
            <span class="inner">Caption3 </span>
          </span>
        </label>
      </li>
    </ul>
  </div>
</body>

</html>

在这里插入图片描述

小声BB:忘记了代码的原地址,有知道的告诉我一声,我附上


原生 JavaScript

在这里插入图片描述

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0 user-scalable=0">
  <title>Document</title>
  <style>
    * {
      margin: 0;
      padding: 0;
      touch-action: pan-y;
    }

    html,
    body {
      height: 100%;
      overflow: hidden;
    }

    #wrap {
      height: 100%;
      overflow: hidden;
    }

    .carousel-wrap {
      position: relative;
      overflow: hidden;
    }

    .carousel-wrap>.list {
      list-style: none;
      overflow: hidden;
      position: absolute;
    }

    .carousel-wrap>.list>li>a,
    .carousel-wrap>.list>li>a>img {
      display: block;
    }

    .carousel-wrap>.list>li>a>img {
      width: 100%;
    }

    .carousel-wrap>.list>li {
      float: left;
    }

    .carousel-wrap>.list {
      overflow: hidden;
    }

    .carousel-wrap>.points-wrap {
      position: absolute;
      bottom: 0;
      width: 100%;
      text-align: center;
      z-index: 1;
    }

    .carousel-wrap>.points-wrap>span {
      width: 10px;
      height: 10px;
      border-radius: 50%;
      background-color: #000;
      display: inline-block;
      margin-left: 5px;
    }

    .carousel-wrap>.points-wrap>span.active {
      background-color: #fff;
    }
  </style>
</head>

<body>
  <div id="wrap">
    <div class="carousel-wrap" needCarousel needAuto>
      <div class="points-wrap"></div>
    </div>
  </div>
  <script>
    (function (w) {
      // w => window
      w.swiper = {}

      w.swiper.css = function css(node, type, val) {
        if (node && typeof node["transform"] === "undefined") {
          node["transform"] = {}
        }
        if (arguments.length >= 3) {
          var text = ""
          node["transform"][type] = val

          for (item in node["transform"]) {
            if (node["transform"].hasOwnProperty(item)) {
              switch (item) {
                case "translateX":
                case "translateY":
                  text += item + "(" + node["transform"][item] + "px)"
                  break;
                case "scale":
                  text += item + "(" + node["transform"][item] + ")"
                  break;
                case "rotate":
                  text += item + "(" + node["transform"][item] + "deg)"
                  break;
              }
            }
          }
          node.style.transform = node.style.webkitTransform = text
        } else if (arguments.length === 2) {
          val = node["transform"][type]
          if (typeof val === "undefined") {
            switch (type) {
              case "translateX":
              case "translateY":
              case "rotate":
                val = 0
              case "scale":
                val = 1
                break;
            }
          }
          return val
        }
      }
      
      w.swiper.carousel = function carousel(arr) {
        // 创建布局
        var carouselWrap = document.querySelector('.carousel-wrap')
        // 
        if (carouselWrap) {
          // 获取图片数组的长度
          var pointslength = arr.length

          // 判断该元素是否需要轮播
          var needCarousel = carouselWrap.getAttribute("needCarousel")
          needCarousel = needCarousel === null ? false : true
          // 连接数组
          if (needCarousel) {
            arr = arr.concat(arr)
          }

          // 创建下方的小圆点
          var ulNode = document.createElement('ul')
          var styleNode = document.createElement('style')
          // ul 添加样式
          ulNode.classList.add('list')
          // 根据 arr 的长度生成 img 标签
          for (let i = 0; i < arr.length; i++) {
            ulNode.innerHTML += `<li>
          <a href="">
            <img src="${arr[i]}" alt="">
          </a>
        </li>`
          }

          // 生成 li 的宽度
          styleNode.innerHTML = `.carousel-wrap>.list>li {width: ${1 / arr.length * 100}%;}
      .carousel-wrap>.list {width: ${arr.length}00%;}`

          // 加入页面中
          carouselWrap.appendChild(ulNode)
          document.head.appendChild(styleNode)

          var imgNodes = document.querySelector('.carousel-wrap>.list>li>a>img')
          // 图片撑满容器
          setTimeout(function () {
            carouselWrap.style.height = imgNodes.offsetHeight + 'px'
          }, 100)

          var pointsWrap = document.querySelector('.carousel-wrap > .points-wrap')

          // 根据小圆点数组的长度,往页面当中插入小圆点。如果是第一个,那默认为选中。
          if (pointsWrap) {
            for (let i = 0; i < pointslength; i++) {
              if (i === 0) {
                pointsWrap.innerHTML += `<span class='active'></span>`
              } else {
                pointsWrap.innerHTML += `<span></span>`
              }
            }
            // 小圆点
            var pointsSpan = document.querySelectorAll('.carousel-wrap > .points-wrap > span')
          }

          // 滑动布局
          var index = 0
          // 手指的位置
          var startX = 0
          // 元素的位置
          var elementX = 0
          var disX = 0

          // 监听 touchstart 事件
          carouselWrap.addEventListener('touchstart', function (ev) {
            ev = ev || event
            var TouchC = ev.changedTouches[0]
            ulNode.style.transition = 'none'

            // 无缝逻辑(点击第一组第一张,瞬间调到第二组第一张)
            // (点击第二组最后一张,瞬间调到第一组最后一张)

            if (needCarousel) {
              var index = swiper.css(ulNode, "translateX") / document.documentElement.clientWidth
              if (-index === 0) {
                index = -pointslength
              } else if (-index === (arr.length - 1)) {
                index = -(pointslength - 1)
              }
              swiper.css(ulNode, "translate", index * document.documentElement.clientWidth)
            }

            startX = TouchC.clientX
            // elementX = translateX
            elementX = swiper.css(ulNode, "translateX")
            // 清除定时器
            clearInterval(timer)

          }, { passive: false })

          // 监听 touchmove 事件
          carouselWrap.addEventListener('touchmove', function (ev) {
            ev = ev || event
            var TouchC = ev.changedTouches[0]
            var nowX = TouchC.clientX
            var disX = nowX - startX

            swiper.css(ulNode, "translateX", elementX + disX)

          }, { passive: false })

          // 监听 touchend 事件
          carouselWrap.addEventListener('touchend', function (ev) {
            ev = ev || event

            // index 代表窗口 ul 的实时位置
            index = swiper.css(ulNode, "translateX") / document.documentElement.clientWidth
            index = Math.round(index)

            // 超出控制
            if (index > 0) {
              index = 0
            } else if (index < 1 - arr.length) {
              index = 1 - arr.length
            }

            dots(index)
            ulNode.style.transition = '1s transform'
            swiper.css(ulNode, "translateX", index * (document.documentElement.clientWidth))

            // 开启自动轮播
            if (needAuto) {
              auto()
            }
          }, { passive: false })

          var timer = 0
          // 抽象图片下标
          var needAuto = carouselWrap.getAttribute("needAuto")
          needAuto = needAuto === null ? false : true

          // 自动轮播
          if (needAuto) {
            auto()
          }

          function auto() {
            clearInterval(timer)
            timer = setInterval(function () {
              if (index === 1 - arr.length) {
                ulNode.style.transition = "none"
                index = 1 - arr.length / 2
                swiper.css(ulNode, "translateX", index * document.documentElement.clientWidth)
              }
              setTimeout(function () {
                index--
                ulNode.style.transition = "1s transform"
                dots(index)
                swiper.css(ulNode, "translateX", index * document.documentElement.clientWidth)
              }, 50)
            }, 2000)
          }

          function dots(index) {
            if (!pointsWrap) {
              return
            }
            for (var i = 0; i < pointsSpan.length; i++) {
              pointsSpan[i].classList.remove('active')
            }
            pointsSpan[-index % pointslength].classList.add('active')
          }
        }
      }
    })(window)
  </script>
  <script>
    window.onload = function () {
      document.addEventListener('touchstart', function (ev) {
        ev = ev || event
        ev.preventDefault()
      });

      let arr = [
        'https://cdn.pixabay.com/photo/2013/07/18/20/26/sea-164989__480.jpg',
        'https://cdn.pixabay.com/photo/2013/07/25/01/31/forest-166733__340.jpg',
        'https://cdn.pixabay.com/photo/2014/10/07/13/48/mountain-477832__340.jpg',
        'https://cdn.pixabay.com/photo/2015/03/28/16/40/lake-696098__340.jpg',
        'https://cdn.pixabay.com/photo/2016/10/18/21/22/beach-1751455__340.jpg'
      ]
      swiper.carousel(arr)
    }
  </script>
</body>

</html>

jQuery

在这里插入图片描述

怎么说呢,现在这个 Vue,React, Angular 框架的时代,用 jQuery多少有点看不起这三个框架。

狗头保命🐾🐾🐾🐾

<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>Document</title>
  <script src="./jq.js"></script>
  <style>
    a,
    ul,
    li {
      list-style: none;
      text-decoration: none;
      margin: 0;
      padding: 0;
    }

    .wrap {
      width: 600px;
      height: 400px;
      margin: 100px auto;
      position: relative;
      overflow: hidden;
    }

    .navs {
      width: 4200px;
      height: 100%;
      position: absolute;
      top: 0;
      left: -600px;
    }

    .navs li {
      width: 600px;
      height: 100%;
      float: left;
    }

    .navs li img {
      width: 100%;
      height: 100%;
    }

    #bots {
      display: inline-block;
      width: 600px;
      height: 40px;
      background: rgba(66, 57, 57, 0.3);
      text-align: center;
      position: absolute;
      bottom: 0;
      left: 0;
    }

    #bots li {
      width: 15px;
      height: 15px;
      border-radius: 50%;
      background: #fff;
      position: relative;
      left: 220px;
      top: 10px;
      margin-left: 10px;
      float: left;
    }

    #bots .active {
      background: #000;
    }
  </style>
</head>

<body>
  <div class="wrap" id="box">
    <ul id='navs' class="navs">
      <li><img src="https://cdn.pixabay.com/photo/2016/10/18/21/22/beach-1751455__340.jpg" /></li>
      <li><img src="https://cdn.pixabay.com/photo/2013/07/18/20/26/sea-164989__480.jpg" /></li>
      <li><img src="https://cdn.pixabay.com/photo/2013/07/25/01/31/forest-166733__340.jpg" /></li>
      <li><img src="https://cdn.pixabay.com/photo/2014/10/07/13/48/mountain-477832__340.jpg" /></li>
      <li><img src="https://cdn.pixabay.com/photo/2015/03/28/16/40/lake-696098__340.jpg" /></li>
      <li><img src="https://cdn.pixabay.com/photo/2016/10/18/21/22/beach-1751455__340.jpg" /></li>
      <li><img src="https://cdn.pixabay.com/photo/2013/07/18/20/26/sea-164989__480.jpg" /></li>
    </ul>
    <ul id="bots">
      <li class="bot active"></li>
      <li class="bot"></li>
      <li class="bot"></li>
      <li class="bot"></li>
      <li class="bot"></li>
    </ul>
  </div>

  <script>
    // 1.图片不断出现/隐藏  不断向左侧移动
    // 2.对应的小圆点背景颜色变红
    let num = 1;
    let i = 0;
    let timer = null;
    swiper();
    
    // 把定时器功能做一个函数封装
    function swiper() {
      timer = setInterval(() => {
        // 1、图片功能
        num++;
        // 设置条件 当num大于6,num=2
        if (num > 6) {
          // num = 6的时候,其实显示  ban1  
          // 应该通过样式赋值的形式,直接变为 真正的 ban1
          $("#navs").css('left', -600)
          num = 2;
        }
        $("#navs").animate({ left: -num * 600 }, 500);

        // 2.小圆点功能
        i++;
        if (i > 4) {
          i = 0;
        }
        $("#bots li").eq(i).addClass('active').siblings().removeClass('active');
      }, 2000);
    }

    // 鼠标移入 清除定时器
    $(".wrap").mouseover(() => {
      clearInterval(timer);
    });

    // 鼠标移出 开启定时器
    $(".wrap").mouseout(() => {
      swiper();
    });
  </script>
</body>

</html>

Vue

实现效果如下
在这里插入图片描述
写之前我们需要安装

npm install better-scroll

slider.vue

<template>
  <div class="slider" ref="slider">
    <div class="slider-group" ref="sliderGroup">
      <slot>
      </slot>
    </div>
    <div class="dots">
      <span
        class="dot"
        v-for="(item, index) in dots"
        :key="index"
        :class="{active: currentPageIndex === index}"
      ></span>
    </div>
  </div>
</template>

<script>
import BScroll from 'better-scroll'
import { addClass } from '../../common/js/dom'
export default {
  data() {
    return {
      dots: [],
      currentPageIndex: 0
    }
  },
  props: {
    loop: {
      type: Boolean,
      default: true
    },
    autoPlay: {
      type: Boolean,
      default: true
    },
    interval: {
      type: Number,
      default: 4000
    }
  },
  mounted() {
    setTimeout(() => {
      this._setSliderWidth()
      this._initDots()
      this._initSlider()

      if (this.autoPlay) {
        this._play()
      }
    }, 20)

    window.addEventListener('resize', () => {
      if (!this.slider) {
        return
      }
      this._setSliderWidth(true)
      this.slider.refresh()
    })
  },
  methods: {
    _setSliderWidth(isResize) {
      this.children = this.$refs.sliderGroup.children
      let width = 0
      let slideWidth = this.$refs.slider.clientWidth
      for (let i = 0; i < this.children.length; i++) {
        let children = this.children[i]

        addClass(children, 'slider-item')
        children.style.width = slideWidth + 'px'
        width += slideWidth
      }
      if (this.loop && !isResize) {
        width += 2 * slideWidth
      }
      this.$refs.sliderGroup.style.width = width + 'px'
    },
    _initSlider() {
      this.slider = new BScroll(this.$refs.slider, {
        scrollX: true,
        scrollY: false,
        momentum: false,
        snap: true,
        snapLoop: this.loop,
        snapThreshold: 0.3,
        snapSpeed: 400
      })

      this.slider.on('scrollEnd', () => {
        let pageIndex = this.slider.getCurrentPage().pageX
        if (this.loop) {
          pageIndex -= 1
        }
        this.currentPageIndex = pageIndex

        if (this.autoPlay) {
          clearTimeout(this.timer)
          this._play()
        }
      })
    },
    _initDots() {
      this.dots = new Array(this.children.length)
    },
    _play() {
      let pageIndex = this.currentPageIndex + 1
      if (this.loop) {
        pageIndex += 1
      }
      this.timer = setTimeout(() => {
        this.slider.goToPage(pageIndex, 0, 400)
      }, this.interval)
    }
  },
  destroyed() {
    clearTimeout(this.timer)
  }
}
</script>

<style scoped lang="stylus">
  .slider
    min-height: 1px
    .slider-group
      position: relative
      overflow: hidden
      white-space: nowrap
      .slider-item
        float: left
        box-sizing: border-box
        overflow: hidden
        text-align: center
        height 200px
        .a
          display: block
          width: 100%
          overflow: hidden
          text-decoration: none
        img
          display: block
          width: 100%
    .dots
      position: absolute
      right: 0
      left: 0
      bottom: 12px
      text-align: center
      font-size: 0
      .dot
        display: inline-block
        margin: 0 4px
        width: 8px
        height: 8px
        border-radius: 50%
        background: rgba(255, 255, 255, 0.5)
        &.active
          width: 20px
          border-radius: 5px
          background: rgba(255, 255, 255, 0.8)
</style>

dom.js

export function addClass(el, className) {
  if (hasClass(el, className)) {
    return
  }
  let newClass = el.className.split(' ')
  newClass.push(className)
  el.className = newClass.join(' ')
}

export function hasClass(el, className) {
  let reg = new RegExp('(^|\\s)' + className + '(\\s|$)')
  return reg.test(el.className)
}

export function getData(el, name, val) {
  const prefix = 'data-'
  name = prefix + name
  if (val) {
    return el.setAttribute(name, val)
  } else {
    return el.getAttribute(name)
  }
}

使用方法

<div v-if="recommends.length" class="slider-wrapper">
  <Slider>
    <div v-for="item in recommends" :key="item.linkUrl">
      <router-link :to="item.linkUrl" class="a">
        <img 
        	@load="loadImage" 
        	v-lazy="item.picUrl"  
        	width="100%" 
        	height="200"
        >
      </router-link>
    </div>
  </Slider>
</div>
import Slider from '../../base/slider/slider'
data() {
  return {
    recommends: [
      {
        "picUrl": "https://cdn.pixabay.com/photo/2020/08/11/13/28/flowers-5479950__340.jpg",
        "linkUrl": '/'
      },
      {
        "picUrl": "https://cdn.pixabay.com/photo/2020/08/08/20/19/wave-5473869__340.jpg",
        "linkUrl": '/a'
      },
      {
        "picUrl": "https://cdn.pixabay.com/photo/2020/07/05/12/53/rainbow-5372892__340.jpg",
        "linkUrl": '/b'
      }
    ]
  }
},
components: {
  Slider
},
methods: {
  loadImage() {
    if (!this.checkloaded) {
      this.checkloaded = true
      this.$refs.scroll.refresh()
    }
  }
}
style

.slider-wrapper
  position: relative
  width: 100%
  overflow: hidden
  height 200px

React

最开始传入图片数据

import Banner from './banner'

let imgUrl = [
  'https://img.zcool.cn/community/010c815e25a8c4a801216518dcc1d4.jpg@1280w_1l_2o_100sh.jpg', 'https://img.zcool.cn/community/0189905e25a8c6a80120a89533cc7f.jpg@1280w_1l_2o_100sh.jpg', 'https://img.zcool.cn/community/01747f5e25a8c9a801216518193452.jpg@1280w_1l_2o_100sh.jpg', 'https://img.zcool.cn/community/01cbba5e25a8cea80120a895743281.jpg@1280w_1l_2o_100sh.jpg', 'https://img.zcool.cn/community/0186025e25a8d3a80121651827af9c.jpg@1280w_1l_2o_100sh.jpg'
]

function App() {
  return (
    <div>
      <Banner
        imgUrl={imgUrl}
      />
    </div>
  );
}

export default App;

基础架构:中间展示图片,两边箭头,下面依据图片数量生成的小圆点

import React from "react"
import "./banner.css"

export default class Banner extends React.Component {

  render() {
    return (
      <div
        className="container"
      >
        <div className="swiper-box">
          {
            this.props.imgUrl.map((item, index) => {
              return (
                <div className="swiper-item" key={index}>
                  <img src={item} alt="" />
                </div>
              )
            })
          }
        </div>

        <div className="swiper-arrow">
          <div className="arrowLeft iconfont iconqianjin1"></div>
          <div className="arrowRight iconfont iconqianjin"></div>
        </div>

        <div className="focus">
          {
            this.props.imgUrl.map((item, index) => {
              return (
                <div></div>
              )
            })
          }
        </div>
      </div>
    )
  }
}

在这里插入图片描述

咦咦咦咦~~~~,so ugly

为实现无缝衔接轮播,我们需要重新去生成一份数组。props 中解构出 imgUrl

constructor(props) {
  super(props)
  let { imgUrl } = this.props
  let imgUrlClone = []
  imgUrlClone = imgUrlClone.concat([], imgUrl)
  imgUrlClone.push(imgUrl[0])
  imgUrlClone.unshift(imgUrl[imgUrl.length - 1])
  console.log(imgUrlClone)
}

一顿操作下来,假设 imgUrl = [A, B, C, D, E] => imgUrlClone = [E, A, B, C, D, E, A]

{
  this.imgUrlClone.map((item, index) => {
    return (
      <div className="swiper-item" key={index}>
        <img src={item} alt="" />
      </div>
    )
  })
}

在这里插入图片描述
接下来,要将图片的显示变为正常样式

let { width, height, step, speed } = this.state;

let swiperBoxStyle = {
  width: `${this.imgUrlClone.length * width}px`,
  left: `${-step * width}px`,
  height: height + "px",
  transition: `all ${speed}ms linear`
}
let commom = {
  width: width + "px",
  height: height + "px"
}

分别加到 className="container"className="swiper-box" 上。
在这里插入图片描述
静态布局结束之后,让我们的图片动起来。

this.state = { speed, step, width, height }

componentDidMount() {
  if (this.props.autoplay) {
    this.timer = setInterval(() => {
      this.next()
    }, this.props.interval);
  }
}

next = () => {
  let { step } = this.state
  if (step >= this.imgUrlClone.length - 1) {
    this.setState({
      step: 1,
      speed: 0
    })
  }
  setTimeout(() => {
    this.setState({
      step: this.state.step + 1,
      speed: this.props.speed
    })
  }, 0)
}

切换小圆点对应的 active 样式

<div className="focus">
  {
    this.props.imgUrl.map((item, index) => {
      if (step === this.imgUrlClone.length - 1) {
        step = 1
      }
      if (step === 0) {
        step = this.imgUrlClone.length - 2
      }
      return (
        <div className={index + 1 === step ? "active" : ""} key={index}></div>
      )
    })
  }
</div>

在这里插入图片描述为左右箭头添加事件

<div className="swiper-arrow">
  <div className="arrowLeft iconfont iconqianjin1" onClick={this.prev}></div>
  <div className="arrowRight iconfont iconqianjin" onClick={this.next}></div>
</div>
prev = () => {
  let { step } = this.state
  if (step <= 0) {
    this.setState({
      step: this.imgUrlClone.length - 2,
      speed: 0
    })
  }
  setTimeout(() => {
    this.setState({
      step: this.state.step - 1,
      speed: this.props.speed
    })
  }, 0)
}

锦上添花,鼠标移入,轮播停止。鼠标移出,轮播继续

<div
  className="container"
  style={commom}
  onMouseEnter={this.removeInte}
  onMouseLeave={this.addInter}
></div>
removeInte = () => {
  clearInterval(this.timer)
}
addInter = () => {
  if (!this.props.autoplay) return
  this.timer = setInterval(() => {
    this.next()
  }, this.props.interval);
}

源代码:React 轮播图组件

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值