Vue简易三页轮播图实现

本文介绍如何使用Vue构建一个简单的三页轮播图组件,包括点击切换、移动逻辑、无缝衔接以及处理移动不协调问题。通过Vue的父子组件通信、CSS的transform属性和边界项设置实现轮播效果,并探讨了快速点击导致的同步问题及其解决方案。

实现效果

在这里插入图片描述
点击切换。可移动一个单位
下面我们思考我们的组件使用形式

    <base-swiper class="swiper">
      <base-swiper-item v-for="item of list" :key="item" :gap="10">
        <div class="box">
          <div class="content">
            <img :src="item" alt="">
          </div>
        </div>
      </base-swiper-item>
    </base-swiper>

可以看到我们的swiper组件必须是成套使用的。在swiper-item里面设置一个slot

轮播图的实现存在的问题。
1、我们怎么才能确定处理swiper跟swiper-item的关系,也就是。我们怎么才能确定swiper跟swiper-item的关系。

2、怎么实现轮播图移动

3、怎么实现轮播图的无缝切换

4、轮播图移动不协调问题

解决方式

1、使用Vue的parent跟parent跟parentchinldren来获取父组件跟子组件,这样我们就要可以在组件里面去获取到我们预设的slot的值了。

2、使用transform

3、设置边界的item。通过设置边界item为-值进行移动切换

4、出现不协调有两种情况。我们最后一个移动到负值的时候,因为是回移动,因为z-index的原因。所以会导致覆盖问题。
2是点击过快的问题,可能会出现,最后一个还没有移动过去,但是第一个已经开始移动了
这个时候我们设置最后一个移动到第一个的时候,无动画。

下面是源代码。
base-swiper.vue

<template>
  <div class="swiper">
    <ul class="list" :style="{height:height+'px'}">
      <slot></slot>
    </ul>
    <i v-show="showTag" class="el-icon-arrow-left icon left" @click="handleSwitch('left')"></i>
    <i v-show="showTag" class="el-icon-arrow-right icon right" @click="handleSwitch('right')"></i>
  </div>
</template>
<script>
export default {
  name: 'BaseSwiper',
  props: {
    height: {
      type: Number,
      default: 120
    },
    // 如果我们希望两个轮播之间存在间隔 设置此参数而不用使用margin
    gap: {
      type: Number,
      default: 0
    },
    delay: {
      type: Number,
      default: 200
    },
    // 是否开启限速 用于处理点击速度过快的问题 目前使用delay 0进行处理,如果希望进行限速 也可以使用限速模式进行处理 即最快delay时间切换一张
    speedLimit: {
      type: Boolean,
      default: false,//默认不开启
    }
  },
  components: {
  },
  data () {
    return {
      offsetWidth: 0,//偏移距离
      offsetArr: [],
      criticalIndex: -1,//临界元素 次元素在移动的时候优先级最低
      showTag: true,
      canSwitch: true,//是否可以进行切换
    }
  },
  computed: {
    // 获取子元素
    items () {
      return this.$children.length > 0 ? this.$children.filter(v => v.$options.name === "BaseSwiperItem") : []
    },
    // 子元素个数
    itemNumber () {
      return this.items.length
    },
  },
  mounted () {
    this.offsetWidth = (this.$el.offsetWidth + this.gap) / 3
    // 获取最开始的时候的排列
    for (let i = 0; i < this.itemNumber; i++) {
      if (i === (this.itemNumber - 1) && this.itemNumber > 3) {
        this.offsetArr.push(-this.offsetWidth)
      } else {
        this.offsetArr.push(i * this.offsetWidth)
      }
    }
    if (this.itemNumber <= 3) {
      this.showTag = false
    }
    this.updateItem()
  },
  methods: {
    updateItem () {
      // 逐个设置item的offsetWidth
      this.offsetArr.forEach((v, index) => {
        // 是否是临界元素
        let isCritical = this.criticalIndex === index
        this.items[index].setOffset(v, isCritical)
      })
    },
    handleSwitch (type) {
      // 如果不可以切换
      if (!this.canSwitch) return
      if (this.speedLimit) {
        this.canSwitch = false
        setTimeout(() => {
          this.canSwitch = true
        }, this.delay)
      }
      // 切换
      // 先全部向一边走
      // let oldOffsetArr = this.offsetArr
      if (this.itemNumber > 4) {
        this.switchExceed5(type)
      } else {
        this.switchUnder5(type)
      }
      // 切换后进行更新
      this.updateItem()
    },
    // 大于等于5张的时候的操作方式
    switchExceed5 (type) {
      if (type === 'left') {
        this.switchExceed5LeftMove()
      } else {
        this.switchExceed5RightMove()
      }
    },
    // 大于等于五张的时候向左移动
    switchExceed5LeftMove () {
      this.offsetArr = this.offsetArr.map((v, index) => {
        // 根据偏移量进行判断 如果已经是负数了 则往回走
        if (v >= (this.itemNumber - 2) * this.offsetWidth) {
          this.criticalIndex = index
          return -this.offsetWidth
        }
        return v + this.offsetWidth
      })
    },
    // 大于等于五张的时候向右移动
    switchExceed5RightMove () {
      this.offsetArr = this.offsetArr.map((v, index) => {
        // 根据偏移量进行判断 如果已经是负数了 则往回走
        if (v < 0) {
          this.criticalIndex = index
          return this.offsetWidth * (this.itemNumber - 2)
        }
        return v - this.offsetWidth
      })
    },
    // 小于五张的时候的操作
    switchUnder5 (type) {
      if (type === 'left') {
        this.switchUnder5LeftMove()
      } else {
        this.switchUnder5RightMove()
      }
    },
    switchUnder5LeftMove () {
      this.criticalIndex = this.offsetArr.findIndex(v => v === this.offsetWidth * (this.itemNumber - 2))
      console.log(this.criticalIndex);
      let item = this.offsetArr.shift()
      this.offsetArr.push(item)
    },
    switchUnder5RightMove () {
      // 此时临界元素偏移量必定最大
      this.criticalIndex = this.offsetArr.findIndex(v => v === -this.offsetWidth)
      console.log(this.criticalIndex);
      let item = this.offsetArr.pop()
      this.offsetArr.unshift(item)
    }
  }
}
</script>
<style scoped lang='stylus'>
.swiper
  position relative
  .list
    overflow hidden
    position relative
  .icon
    position absolute
    border-radius 6px
    z-index 99
    top 50%
    transform translateY(-50%)
    font-size 30px
    color #fff
    background #000 - rgba(0, 0, 0, 0.7)
    transition background-color 0.3s
  .icon:hover
    background #000 - rgba(0, 0, 0, 0.3)
  .left
    left 0px
  .right
    right 0px
</style>

base-swiper-item

<template>
  <div
    class="base-swiper-item"
    :style="{
    width:$parent.offsetWidth+'px',
    transform:`translateX(${ offsetWidth }px)`,
    transition:`transform ${delay}ms`,
    'z-index':zIndex
  }"
  >
    <slot></slot>
  </div>
</template>
<script>
export default {
  name: 'BaseSwiperItem',
  components: {
  },
  data () {
    return {
      offsetWidth: 0,
      zIndex: 1,//默认的都是1 如果是最后一个需要向前移动的时候 则是-1来实现隐藏
    }
  },
  computed: {
    delay () {
      return this.zIndex === -1 ? 0 : this.$parent.delay
    }
  },
  methods: {
    setOffset (v, isCritical) {
      this.zIndex = isCritical ? -1 : 1
      this.offsetWidth = v
    }
  }
}
</script>
<style scoped lang='stylus'>
.base-swiper-item
  position absolute
</style>
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值