Vue3封装一个轮播图组件

先看效果
在这里插入图片描述

编写组件代码 CarouselChart.vue

<template>
  <div class='img-box'>
    <el-button @click='previousImages' v-if='props.showBtn'></el-button>
    <div class='img'>
      <div style='display: flex;gap: 20px' id='move'>
        <img
          class='img-item' v-for='(item) in props.imgList'
          :key='item.src'
          :src='item.src'
          alt='' />
      </div>
    </div>
    <el-button @click='nextImages' v-if='props.showBtn'></el-button>

    <div class='spots' v-if='props.showSpot'>
      <div
        class='spot'
        v-for='(item,index) in props.imgList.length / 2'
        :key='index'
        :class="{
          'active':index*2 === currentIndex
        }"
        @click='setCurIndex(index)'
      >
      </div>
    </div>
  </div>
</template>

<script setup>
import { defineProps, defineEmits } from 'vue'

const emit = defineEmits(['handlePrev', 'handleNext'])
const props = defineProps({
  // 图片
  imgList: {
    type: Object,
    required: true,
  },
  // 是否显示左右切换按钮
  showBtn: {
    type: Boolean,
    default: true,
  },
  // 是否显示小圆点
  showSpot: {
    type: Boolean,
    default: true,
  },
})

const currentIndex = ref(0)

const updateVisibleImages = () => {
  const imgDom = document.querySelector('#move')
  imgDom.style.transform = `translateX(-${((currentIndex.value * 440) + ((currentIndex.value) * 20))}px)`
  imgDom.style.transition = `all 0.5s`
}


const nextImages = () => {
  if (currentIndex.value + 2 >= props.imgList.length) return
  currentIndex.value += 2
  updateVisibleImages()
  emit('handleNext')
}

const previousImages = () => {
  if (currentIndex.value <= 0) return
  currentIndex.value -= 2
  updateVisibleImages()
  emit('handlePrev')
}

const setCurIndex = (index) => {
  currentIndex.value = index * 2
  updateVisibleImages()
}

</script>

<style scoped lang='scss'>
.img-box {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 20px;
  width: 100%;
  height: 100%;
  position: relative;

  .img {
    width: 900px;
    overflow: hidden;
    transition: all 0.5s;

    .img-item {
      width: 440px;

      img {
        width: 100%;
      }
    }
  }

  .spots {
    position: absolute;
    left: 50%;
    margin-top: 20%;
    transform: translateX(-50%);
    display: flex;
    gap: 20px;
    align-items: center;
    justify-content: center;


    .spot {
      width: 10px;
      height: 10px;
      border-radius: 5px;
      background-color: #1a60ea;
      transition: all 0.5s;
      cursor: pointer;

      &:hover {
        transform: scale(1.5);
      }
    }

    .active {
      background-color: #ea1ccc;
      transform: scale(2);
      transition: all 0.5s;
    }
  }
}
</style>

使用这个组件

<template>
  <div class='h-full w-full flex align-center justify-center'>
    <CarouselChart
      :img-list='imgList'
      @handlePrev='handlePrev'
      @handleNext='handleNext'
    />
  </div>
</template>
<script setup>
import { reactive } from 'vue'
import { ElMessage } from 'element-plus'
import img1 from '@/assets/lunbo/01.jpg'
import img2 from '@/assets/lunbo/02.jpg'
import img3 from '@/assets/lunbo/03.jpg'
import img4 from '@/assets/lunbo/04.jpg'
import img5 from '@/assets/lunbo/05.jpg'
import img6 from '@/assets/lunbo/06.jpg'
import img7 from '@/assets/lunbo/07.jpg'
import img8 from '@/assets/lunbo/08.jpg'
import img9 from '@/assets/lunbo/09.jpg'
import img10 from '@/assets/lunbo/10.jpg'

let imgList = reactive([
  { src: img1 },
  { src: img2 },
  { src: img3 },
  { src: img4 },
  { src: img5 },
  { src: img6 },
  { src: img7 },
  { src: img8 },
  { src: img9 },
  { src: img10 },
])

function handleNext() {
  ElMessage.warning('下一张')
}

function handlePrev() {
  ElMessage.success('上一张')
}

</script>
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
好的,以下是一个简单的 Vue2 轮组件封装: ```vue <template> <div class="slider"> <div class="slider__wrapper" :style="{ transform: 'translateX(' + translateX + 'px)' }"> <div v-for="(item, index) in items" :key="index" class="slider__item"> <img :src="item.imgUrl" alt=""> </div> </div> <div class="slider__dots"> <span v-for="(item, index) in items" :key="index" :class="{ active: currentIndex === index }" @click="handleDotClick(index)"></span> </div> <button class="slider__btn slider__btn--prev" @click="prev"> <i class="iconfont icon-arrow-left"></i> </button> <button class="slider__btn slider__btn--next" @click="next"> <i class="iconfont icon-arrow-right"></i> </button> </div> </template> <script> export default { props: { items: { type: Array, required: true }, interval: { type: Number, default: 3000 } }, data() { return { currentIndex: 0, translateX: 0, timer: null } }, computed: { wrapperWidth() { return this.items.length * 100 + '%' }, itemWidth() { return 100 / this.items.length + '%' } }, mounted() { this.startAutoPlay() }, beforeDestroy() { this.stopAutoPlay() }, methods: { startAutoPlay() { this.timer = setInterval(() => { this.next() }, this.interval) }, stopAutoPlay() { clearInterval(this.timer) }, prev() { this.currentIndex = (this.currentIndex + this.items.length - 1) % this.items.length this.translateX = -this.currentIndex * this.$el.clientWidth }, next() { this.currentIndex = (this.currentIndex + 1) % this.items.length this.translateX = -this.currentIndex * this.$el.clientWidth }, handleDotClick(index) { this.currentIndex = index this.translateX = -this.currentIndex * this.$el.clientWidth } } } </script> <style> .slider { position: relative; overflow: hidden; } .slider__wrapper { display: flex; transition: transform 0.3s ease-out; } .slider__item { width: 100%; flex-shrink: 0; } .slider__dots { position: absolute; bottom: 20px; left: 50%; transform: translateX(-50%); display: flex; } .slider__dots span { width: 10px; height: 10px; border-radius: 50%; background-color: #ccc; margin-right: 5px; cursor: pointer; transition: background-color 0.3s ease-out; } .slider__dots span.active { background-color: #333; } .slider__btn { position: absolute; top: 50%; transform: translateY(-50%); width: 30px; height: 30px; background-color: rgba(0, 0, 0, 0.3); border: none; border-radius: 50%; color: #fff; cursor: pointer; transition: background-color 0.3s ease-out; } .slider__btn:hover { background-color: rgba(0, 0, 0, 0.5); } .slider__btn i { font-size: 16px; line-height: 30px; } .slider__btn--prev { left: 20px; } .slider__btn--next { right: 20px; } </style> ``` 使用时,可在父组件中引入并传入轮数据: ```vue <template> <slider :items="items" :interval="3000" /> </template> <script> import Slider from '@/components/Slider.vue' export default { components: { Slider }, data() { return { items: [ { imgUrl: 'https://picsum.photos/800/400?random=1' }, { imgUrl: 'https://picsum.photos/800/400?random=2' }, { imgUrl: 'https://picsum.photos/800/400?random=3' } ] } } } </script> ```

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值