组件封装 - 轮播图组件

轮播图组件封装, 我们主要分为三大步来完成:

1. 基本布局

2. 渲染结构

3. 逻辑封装

我们这里封装的轮播图组件和 Element UI 所封装的走马灯组件有所不同

Element UI 是直接渲染组件来决定的, 我们所封装的是根据数据来决定

首先我们来完成第一大步(基本布局)

思路分析:

1. 首先需要一个父盒子对轮播图组件进行约束, 父盒子根据情况来是否给定位; 设置宽高

2. 轮播图组件根据外部的父盒子的宽高走, 设置100%; 自身设置相对定位

3. 通过样式去设置好左右切换图片的按钮位置, 用户自定义图片位置的圆点位置和显示图片的位置

4. 显示的图片我们可以将所有的图片叠在一起, 定义一个 fade 类; 通过图片的下标值来动态的控制 opacity 类的值

5. 还需要设置 active 类, 用户点击圆点的时候; 将点击的那一个高亮

<template>
  <div class='xtx-carousel'>
    <ul class="carousel-body">
      <!-- 所有图片列表 -->
      <li class="carousel-item fade">
        <RouterLink to="/">
          <img src="http://yjy-xiaotuxian-dev.oss-cn-beijing.aliyuncs.com/picture/2021-04-15/1ba86bcc-ae71-42a3-bc3e-37b662f7f07e.jpg" alt="">
        </RouterLink>
      </li>
    </ul>
    <!-- 左箭头 -->
    <a href="javascript:;" class="carousel-btn prev"><i class="iconfont icon-angle-left"></i></a>
    <!-- 右箭头 -->
    <a href="javascript:;" class="carousel-btn next"><i class="iconfont icon-angle-right"></i></a>
    <!-- 计数器 -->
    <div class="carousel-indicator">
      <span v-for="i in 5" :key="i"></span>
    </div>
  </div>
</template>

<script>
export default {
  name: 'XtxCarousel'
}
</script>

<style scoped lang="less">
.xtx-carousel{
  width: 100%;
  height: 100%;
  min-width: 300px;
  min-height: 150px;
  position: relative;
  .carousel{
    &-body {
      width: 100%;
      height: 100%;
    }
    &-item {
      width: 100%;
      height: 100%;
      position: absolute;
      left: 0;
      top: 0;
      // 默认不显示任何一张图片
      opacity: 0;
      // 显示图片的一个淡入效果
      transition: opacity 0.5s linear;
      // 切换时, 根据条件添加fade类
      &.fade {
        opacity: 1;
        z-index: 1;
      }
      img {
        width: 100%;
        height: 100%;
      }
    }
    &-indicator {
      position: absolute;
      left: 0;
      bottom: 20px;
      z-index: 2;
      width: 100%;
      text-align: center;
      span {
        display: inline-block;
        width: 12px;
        height: 12px;
        background: rgba(0,0,0,0.2);
        border-radius: 50%;
        cursor: pointer;
        ~ span {
          margin-left: 12px;
        }
        // 通过条件判断, 动态设置active类
        &.active {
          background:  #fff;
        }
      }
    }
    &-btn {
      width: 44px;
      height: 44px;
      background: rgba(0,0,0,.2);
      color: #fff;
      border-radius: 50%;
      position: absolute;
      top: 228px;
      z-index: 2;
      text-align: center;
      line-height: 44px;
      // 默认将左右箭头隐藏起来
      opacity: 0;
      transition: all 0.5s;
      &.prev{
        left: 20px;
      }
      &.next{
        right: 20px;
      }
    }
  }
  &:hover {
    // 鼠标经过的时候, 显示出来
    .carousel-btn {
      opacity: 1;
    }
  }
}
</style>

 

第二步(渲染结构)

思路分析:

1. 我们结构是根据后端的数据来的, 所以轮播图组件需要暴露出一个 sliders 出去接收父组件传入来的数据

2. 轮播图组件内部还需要暴露出一个记录下标值的响应式变量, 它的默认值是 0 , 默认显示第一种图片和默认高亮第一个计步器按钮

<template>
  <div class='xtx-carousel'>
    <ul class="carousel-body">
      <!-- 所有图片列表 -->
      <li class="carousel-item" v-for="(item, i) in sliders" :key="item.id" :class="{fade: i===index}">
        <RouterLink to="/">
          <img :src="item.imgUrl" alt="">
        </RouterLink>
      </li>
    </ul>
    <!-- 左箭头 -->
    <a href="javascript:;" class="carousel-btn prev"><i class="iconfont icon-angle-left"></i></a>
    <!-- 右箭头 -->
    <a href="javascript:;" class="carousel-btn next"><i class="iconfont icon-angle-right"></i></a>
    <!-- 计数器 -->
    <div class="carousel-indicator">
      <span v-for="(item, i) in sliders" :key="item" :class="{active: i===index}"></span>
    </div>
  </div>
</template>

<script>
import { ref } from 'vue'
export default {
  name: 'XtxCarousel',
  props: {
    sliders: {
      type: Array,
      default: () => []
    }
  },
  setup () {
    // 记录图片的下标值变量
    const index = ref(0)

    return { index }
  }
}
</script>

 

第三步(逻辑封装)

先梳理一下大致要实现的效果:

1. 需要兼容自动轮播效果

2. 鼠标进入停止自动轮播, 鼠标离开继续

3. 点击左右切换按钮, 切换前后图片

4. 点击计步器时, 切换对应的图片

5. 组件销毁时, 清除定时器

第一个需求(兼容自动轮播)

思路分析:

1. 首先是否自动轮播是用户来决定的, 且轮播时间间隔也是一样

2. 所以, 轮播图组件内部需要暴露出 autoPlay 和 duration 两个属性

3. 将自动轮播效果封装成一个函数

4. 使用定时器, 通过修改 index 值来控制图片的切换

5. 调用自动轮播是有条件的, sliders 是否有数据(没有图片的时候, 去做轮播是没有必要的); autoPlay 的值是否为真

<script>
import { ref, watch } from 'vue'
export default {
  name: 'XtxCarousel',
  props: {
    // 轮播图数据
    sliders: {
      type: Array,
      default: () => []
    },
    // 是否制动轮播
    autoPlay: {
      type: Boolean,
      default: false
    },
    // 轮播时间间隔
    duration: {
      type: Number,
      default: 3000
    }
  },
  setup (props) {
    // 记录图片的下标值变量
    const index = ref(0)

    let timer = null
    const autoPlayFn = () => {
      // 清除掉上一个定时器, 避免定时器的累加
      // 特别是开发热更新的项目时
      clearInterval(timer)
      timer = setInterval(() => {
        index.value++
        if (index.value >= props.sliders.length) {
          index.value = 0
        }
      }, props.duration)
    }

    // 监听sliders值的变化
    watch(() => props.sliders, (newVal) => {
      if (newVal.length && props.autoPlay) {
        autoPlayFn()
      }
      // 加上immediate的含义在于, 可能父组件传进来的数据并不是发请求获取的数据
      // 可能是定死的数据, 那么组件初始化的时候就会有数据
      // 不加上immediate的话, 就永远监听不到sliders的变化的了
      // 那么永远就不会做轮播
    }, { immediate: true })

    return { index }
  }
}
</script>
// 因为autoPlay是一个布尔值, 所以直接写autoPlay就代表为true
<XtxCarousel :sliders="sliders" autoPlay />

 

第二个需求(鼠标进入停止轮播, 离开继续)

思路分析:

1. 定义一个 stop 函数, 判断如果 timer 存在; 则清除定时器

2. 定义一个 start 函数, 鼠标离开时开启轮播

3. @mouseenter事件调用 stop 函数, @mouseleave事件调用start函数

<template>
  <div class='xtx-carousel' @mouseenter="stop" @mouseleave="start">
    <ul class="carousel-body">
      <!-- 所有图片列表 -->
      <li class="carousel-item"
        v-for="(item, i) in sliders" :key="item.id"
        :class="{fade: i===index}"
      >
        <RouterLink to="/">
          <img :src="item.imgUrl" alt="">
        </RouterLink>
      </li>
    </ul>

    ......
  </div>
</template>

<script>
import { ref, watch } from 'vue'
export default {
  name: 'XtxCarousel',
  props: {
    // 轮播图数据
    sliders: {
      type: Array,
      default: () => []
    },
    // 是否制动轮播
    autoPlay: {
      type: Boolean,
      default: false
    },
    // 轮播时间间隔
    duration: {
      type: Number,
      default: 1000
    }
  },
  setup (props) {
    ......

    // 鼠标进入停止轮播
    const stop = () => {
      if (timer) clearInterval(timer)
    }
    // 鼠标离开开始轮播
    const start = () => {
      if (props.sliders.length && props.autoPlay) {
        autoPlayFn()
      }
    }

    return { index, stop, start }
  }
}
</script>

 

第三个需求(点击左右切换按钮, 切换图片)

思路分析:

1. 封装一个 toggle 函数, 此函数接收一个 step 数据

2. 用户点击上一张按钮时 step 值为 -1, 下一张按钮时 step 值为 1

<template>
  <div class='xtx-carousel' @mouseenter="stop" @mouseleave="start">
    ......

    <!-- 左箭头 -->
    <a @click="toggle(-1)" href="javascript:;" class="carousel-btn prev"><i class="iconfont icon-angle-left"></i></a>
    <!-- 右箭头 -->
    <a @click="toggle(1)" href="javascript:;" class="carousel-btn next"><i class="iconfont icon-angle-right"></i></a>

    ......
  </div>
</template>

<script>
import { ref, watch } from 'vue'
export default {
  name: 'XtxCarousel',
  ......

  setup (props) {
    ......

    // 点击左右切换按钮, 切换图片
    const toggle = (step) => {
      // 重新定义一个变量的原因是, 可能index.value + step的值会大于可控范围
      // 所以需要定义新的变量, 判断
      const newIndex = index.value + step
      if (newIndex > props.sliders.length - 1) {
        index.value = 0
        return
      }
      if (newIndex <= 0) {
        index.value = props.sliders.length - 1
        return
      }
      // 正常情况
      index.value = newIndex
    }

    return { index, stop, start, toggle }
  }
}
</script>

 

第四个需求(点击计步器切换按钮, 切换图片)

点击时, 将对于的下标值赋值

<!-- 计数器 -->
<div class="carousel-indicator">
    <span 
        @click="index=i" 
        v-for="(item, i) in sliders" :key="item" 
        :class="{active: i===index}">
    </span>
</div>

 

第五个需求(组件销毁时清除定时器)

<script>
// 组件销毁时, 清除定时器
onUnmounted(() => {
    clearInterval(timer)
})
</script>

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值