美文网首页VUE学习
Vue | 写一个轮播图组件

Vue | 写一个轮播图组件

作者: cemcoe | 来源:发表于2020-05-28 20:31 被阅读0次

    轮播图的原理在 CSS 效果 | 轮播图 有提到。不多说,先看效果。

    整体效果 鼠标移入暂停 按钮控制

    1.准备图片素材

    banner1.jpg banner2.jpg banner3.jpg

    2.书写结构

    好嘞,先将结构完成,要点,将图片元素搞到同一行,移动包裹图片的元素控制显示的图片。


    <template>
      <div class="banner-container">
        <ul class="images">
          <li>
            <a href=""><img src="" alt=""/></a>
          </li>
          <li>
            <a href=""><img src="" alt=""/></a>
          </li>
          <li>
            <a href=""><img src="" alt=""/></a>
          </li>
        </ul>
        <ul class="dots">
          <li></li>
          <li></li>
          <li></li>
        </ul>
      </div>
    </template>
    

    样式不是本文的重点,这里直接给出对应的样式:

    .banner-container {
      height: 480px;
      position: relative;
      overflow: hidden;
    }
    .banner-container li {
      display: flex;
      width: 1080px;
      height: 100%;
      float: left;
    }
    .images {
      height: 100%;
      transition: 0.5s;
    }
    .banner-container img {
      width: 100%;
      height: 100%;
    }
    .dots {
      position: absolute;
      top: 0px;
      left: 0px;
    }
    .dots li {
      width: 40px;
      height: 40px;
      background-color: rgb(245, 234, 230);
    }
    .dots .active {
      width: 40px;
      height: 40px;
      background-color: #000;
    }
    

    这里设置了每个图片的宽度是 1080px ,那么包裹这些图片的盒子宽度我们就设置为 1080px * 3,要移动图片直接修改大盒子的 margin-left 值就好。

    但这里有个问题呀,就是封装组件是为了以后可以使用方便,现在图片个数是写死的,这可不行,现在假设父组件得到的轮播图数据的结构如下:

    data() {
      return {
        banners: [
          { url: require("./assets/banner/banner1.jpg"), link: "https://xxx.com" },
          { url: require("./assets/banner/banner2.jpg"), link: "https://ddd.com" },
          { url: require("./assets/banner/banner3.jpg"), link: "https://ccc.com" }
        ]
      };
    }
    

    通过父子组件传值,现在轮播图组件拿到了要轮播的数据

    props: {
      banners: {
        type: Array
      }
    },
    

    现在我们来改写结构。

    <template>
      <div class="banner-container">
        <ul
          class="images"
          :style="{
            width: banners.length * 100 + '%',
            marginLeft: -index * 100 + '%',
          }"
        >
          <li v-for="(item, i) in banners" :key="i">
            <a :href="item.link">
              <img :src="item.url" />
            </a>
          </li>
        </ul>
        <ul class="dots">
          <li v-for="(item, i) in banners" :key="i"></li>
        </ul>
      </div>
    </template>
    

    改写的点在,首先,包裹图片的盒子宽度应该随着图片的个数动态变化,控制的小白点的数量要和图片的数量相同并且图片和小白点要一一绑定。

    3.自动切换

    data() {
      return {
        // 当前是第几张
        index: 0,
        timer: null
      };
    },
    methods: {
      // 自动切换
        this.timer = setInterval(() => {
          this.index = (this.index + 1) % this.banners.length;
        }, 2000);
      }
    },
    created() {
      // 创建成时开始切换
      this.autoStart()
      }
    }
    

    没有什么特别的,在 data 中定义当前的图片索引,在 Vue 的 created 的生命周期中调用在 methods 中定义的 定时器函数。

    这里的关键点是准备的轮播图轮播完了以后如何操作。

    简化一下问题,当 index = 0 时,即第一张图片显示,毫无疑问下一个应该时 index = 1,那么问题来了,当 index = 4 时,应该显示第几章图片?

    这里的解决方法有两种一种是,我在 index 中加一个判断,我就不让它超出图片的数量,当它指向最后一张图片时我将它重置为 0,即展示完最后一张,你下一个给我展示第一张。

    let index = 0;
    setInterval(function() {
      index++;
      if (index > this.banner.length) {
        index = 0;
      }
    }, 1000);
    

    这里假设 this.banner.length = 3,那么这样 index 的值就是 0,1,2,0,1,2...

    有没有更优雅的方法呢?有的,这里可以使用取余操作。

    this.timer = setInterval(() => {
      this.index = (this.index + 1) % this.banners.length;
    }, 2000);
    

    这里不妨也假设 this.banner.length = 3,当组件 created 时,index 默认为 0,带入上面的代码,在 2s 后,index 的值将变为 1。当轮播到最后一张图片时 index 值为 2,待入代码 2 秒后 index 将变回 0,那么这样 index 的值就是 0,1,2,0,1,2...

    不出意外的情况下,现在的轮播图组件的基本功能自动轮播且不限图片数量的功能就是实现了。

    4.鼠标移入暂停轮播,鼠标移出恢复轮播

    明显我们要添加事件监听 mouseenter 和 mouseleave。

     methods: {
        // 自动切换
        autoStart() {
          if (this.timer) {
            return;
          }
          this.timer = setInterval(() => {
            this.index = (this.index + 1) % this.banners.length;
          }, 2000);
        },
        // 停止自动切换
        autoStop() {
          clearInterval(this.timer);
          this.timer = null;
        }
      },
    

    5.点击小白块展示相应的图片

    <ul class="dots">
      <li
        v-for="(item, i) in banners"
        :key="i"
        :class="{active: i === index}"
        @click="index=i"
      ></li>
    </ul>
    

    这是涉及到类名的绑定和事件监听,为了方便将一些判断逻辑直接写到了结构中,当小白块被点击时会添加 active 类型,并被应用样式,同时会更该 index 值,即切换图片。

    附上除样式外的代码:

    <template>
      <div class="banner-container"
      @mouseenter="autoStop"
      @mouseleave="autoStart">
      <!-- 鼠标移入时暂停自动切换 -->
        <ul
          class="images"
          :style="{
          width: banners.length * 100 + '%',
          marginLeft: -index * 100 + '%'
          }"
        >
          <li v-for="(item, i) in banners" :key="i">
            <a :href="item.link">
              <img :src="item.url" />
            </a>
          </li>
        </ul>
        <ul class="dots">
          <li v-for="(item, i) in banners" :key="i" :class="{active: i === index}" @click="index=i"></li>
        </ul>
      </div>
    </template>
    
    <script>
    // 思考哪些数据放到js中管理
    // 有哪些图片,每张图片的超链接地址,当前显示的是第几张
    // [{url: "", link: ""}]
    export default {
      name: "Banner",
      props: {
        banners: {
          type: Array
        }
      },
      data() {
        return {
          // 当前是第几张
          index: 0,
          timer: null
        };
      },
      methods: {
        // 自动切换
        autoStart() {
          if (this.timer) {
            return;
          }
          this.timer = setInterval(() => {
            this.index = (this.index + 1) % this.banners.length;
          }, 2000);
        },
        // 停止自动切换
        autoStop() {
          clearInterval(this.timer);
          this.timer = null;
        }
      },
      created() {
        // 创建成时开始切换
        this.autoStart()
      },
      destroyed() {
        this.autoStop()
      }
    };
    </script>
    

    相关文章

      网友评论

        本文标题:Vue | 写一个轮播图组件

        本文链接:https://www.haomeiwen.com/subject/bajoahtx.html