持续创作,加速成长!这是我参与「掘金日新计划 · 6 月更文挑战」的第4天,点击查看活动详情
前言
最近,公司官网改版,整的花里胡哨的。既然花,那就免不了有很多轮播设计,很多的交互也比较复杂。今天就来浅谈一下最近写的轮播吧。
提起轮播,大家可能第一反应就是swiper.js,哈,我也不例外。因为公司项目使用nuxt,那就很自然的,轮播我首选vue-awesome-swiper了。由于项目比较早,所以swiper版本也比较低。低版本swiper很多花哨的交互也满足不了。就得我们自己手写实现了。
实现
调用swiper库
首先,先看看基础设计吧。
看到这个设计,是不是想到了swiper中的这个demo?
刚开始我也是这样想的,等到实际应用时,问题频出。我们可以确定的事,目前两个轮播的option是这样的。
<div ref="swiper" v-swiper:mySwiper="swiperOption" class="mySwiper">
div class="swiper-wrapper">
<div
v-for="(item, index) in list"
:key="index"
class="swiper-slide"
>
<img :src="item.img" alt="img" class="item-img">
</div>
</div>
</div>
<div
ref="bottomSwiper"
v-swiper:bottomSwiper="bottomSwiperOption"
class="bottomSwiper"
>
<div class="swiper-wrapper">
<div
v-for="(item, index) in list"
:key="index"
class="swiper-slide"
>
<img :src="item.img" alt="img" class="item-img">
</div>
</div>
</div>
computed: {
// 大图轮播
swiperOption() {
return {
};
},
// 底部轮播
bottomSwiperOption() {
return {
};
},
},
而官方例子中,两个轮播这样关联:
var swiper = new Swiper(".mySwiper", {
loop: true,
spaceBetween: 10,
slidesPerView: 4,
freeMode: true,
watchSlidesProgress: true,
});
var swiper2 = new Swiper(".mySwiper2", {
loop: true,
spaceBetween: 10,
navigation: {
nextEl: ".swiper-button-next",
prevEl: ".swiper-button-prev",
},
thumbs: { // thumbs组件专门用于制作带缩略图的swiper
swiper: swiper,
},
});
那按照官方我们是不是应该这样实现?
computed: {
// 大图轮播
swiperOption() {
const self = this;
return {
thumbs: {
swiper: self.bottomSwiperOption,
},
};
},
// 底部轮播
bottomSwiperOption() {
return {
};
},
},
很不幸的事,这样会报错。同样的方式,使用controller 来绑定两个轮播同样会报错。那可能大家会说,这样肯定不行,得用ref来获取绑定。啊,我也尝试了,可惜在内部得到的结果是这样this.$refs.swiper.swiper === undefined。啊这,难道没办法了吗?不能用轮子了吗?哎,当然不是,有请slideTo与slideChangeTransitionStart上场。此时,我们的options就变成了这样。
computed: {
// 大图轮播
swiperOption() {
},
// 底部轮播
bottomSwiperOption() {
const self = this;
return {
// 每一次轮播展示的图片数
slidesPerView: 6,
// 点击卡片切换
slideToClickedSlide: true,
// 循环
loop: self.list.length > 1,
// 自动播放
autoplay: {delay: 2000, disableOnInteraction: false},
// 卡片之间间距
spaceBetween: 20,
// 鼠标形状改变
grabCursor: true,
on: {
// 切换开始前回调
slideChangeTransitionStart() {
const index = this.activeIndex - 6;
if (index === that.list.length) {
that.$refs.swiper.swiper.slideTo(0);
}
else if (index === -1) {
that.$refs.swiper.swiper.slideTo(that.list.length - 1);
}
else {
that.$refs.swiper.swiper.slideTo(index);
}
},
},
};
},
},
这样就简单的实现了底部轮播控制上边轮播展示。
手写
如果仅仅是这样,那也不值得写一篇文章了,有趣的来了,由于底部轮播效果是往前顶,类似于走马灯的效果,UI不满意,UI想要一个一个往后走,走到最大显示个数,保持最后一个选中不动,往前顶一位,直到最后一位下来又回滚到第一位。emmm,好像也不难对吧,写个简单的demo实现一下吧。
先看看效果图。
先来确定页面结构。
<div class="box">
<div class="boxs guodu" ref="swiper">
<div class="itemBox" @click="boxClick">
<div v-for="(item, i) in list" class="items" :key="i">
<div :class="index === i ? 'name active' : 'name'" :index="i">
{{item.name}}
</div>
</div>
</div>
</div>
<button @click="clickright">+1</button>
<button @click="clickleft">-1</button>
</div>
再来写点简单的样式以及过渡效果。
.box {
width: 100%;
height: 100%;
}
.boxs {
width: 1100px;
position: relative;
}
.itemBox {
height: 160px;
overflow: hidden;
}
.items {
margin-right: 20px;
display: inline-block;
}
.name {
width: 160px;
height: 160px;
background-color: #abc;
text-align: center;
line-height: 160px;
font-size: 50px;
}
.active {
width: 180px;
background-color: pink;
}
.guodu {
transition: left .2s, width .2s;
}
然后到了重头戏。js实现简单的交互。先来实现下一张的逻辑
// 通过index来确定当前哪一个是被选中的
clickright() {
// 每次点击index都会加1
this.index++
const box = this.$refs.swiper
// 最大显示6个,当大于6个小于list长度时,控制左移一个格子
if(this.index > 5 && this.index < this.list.length) {
box.style.left = (-180 * (this.index - 5)) + 'px'
box.style.width = box.clientWidth + 180 + 'px'
}
// 超过length,回到第一个
if(this.index >= this.list.length) {
this.index = 0
box.style.left = 0 + 'px'
box.style.width = 1100 + 'px'
}
},
然后就是上一张的逻辑
clickleft() {
// 点击上一张按钮,index减1
this.index--
const box = this.$refs.swiper
// 如果小于0,需要回到尾部
if(this.index < 0) {
this.index = this.list.length - 1
if(this.list.length > 6){
box.style.left = -180 * (this.list.length - 6) + 'px'
box.style.width = 1100 + 180 * ((this.list.length - 6)) + 'px'
}
}else if(this.index > 4) {
// 可以思考一下为什么与4比较,尾部往前移,右移一格距离
box.style.left = (-180 * (this.index - 5)) + 'px'
box.style.width = box.clientWidth - 180 + 'px'
}
},
还有就是点击任意格切换的逻辑
boxClick(e) {
const index = e.target.attributes?.index?.value
const box = this.$refs.swiper
// 确定index然后切换就行
if(index){
if(index < 6) {
box.style.left = 0 + 'px'
box.style.width = 1100 + 'px'
}
this.index = +index
}
}
再来就是让他自动切换的逻辑了
mounted() {
this.swiperLoop()
},
clickright() {
... 逻辑代码
this.swiperLoop()
}
clickleft() {
... 逻辑代码
this.swiperLoop()
}
boxClick(e) {
const index = e.target.attributes?.index?.value
if(index){
this.index = +index
this.swiperLoop()
}
}
swiperLoop() {
clearInterval(this.interval)
this.interval = setTimeout(this.clickright, 2000)
}
这样我们底部就按照预期完成了。至于与顶部关联就留给各位自己实现吧。
总结
本文记载了笔者最近在项目中碰到的轮播,在实现过程中碰到的一些问题与解决方法(仅个人观点),有更好的方法欢迎评论区留言。