实现效果

点击切换。可移动一个单位
下面我们思考我们的组件使用形式
<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跟parent跟chinldren来获取父组件跟子组件,这样我们就要可以在组件里面去获取到我们预设的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>
本文介绍如何使用Vue构建一个简单的三页轮播图组件,包括点击切换、移动逻辑、无缝衔接以及处理移动不协调问题。通过Vue的父子组件通信、CSS的transform属性和边界项设置实现轮播效果,并探讨了快速点击导致的同步问题及其解决方案。
3857

被折叠的 条评论
为什么被折叠?



