这里使用的是vue-awesome-swiper3.1.3
这是官网的例子
<template>
<div class="imageGroup clearfix">
<swiper :options="swiperOptionTop" class="gallery-top" ref="swiperTop">
<swiper-slide v-for="(item, index) in imgList" :key="index">
<img :src="item" class="img" />
</swiper-slide>
</swiper>
<!-- swiper2 Thumbs -->
<div class="thumbsBox">
<i @click="change('prev')" class="el-icon-arrow-left prevBtn" :class="{ btnDisabled: swiperobj1.activeIndex === 0 }"></i>
<swiper :options="swiperOptionThumbs" class="gallery-thumbs" ref="swiperThumbs">
<swiper-slide v-for="(item, index) in imgList" :key="index">
<img :src="item" class="img" />
</swiper-slide>
</swiper>
<i @click="change('next')" class="el-icon-arrow-right nextBtn" :class="{ btnDisabled: swiperobj1.activeIndex === imgList.length - 1 }"></i>
</div>
</div>
</template>
<script>
import 'swiper/dist/css/swiper.css'
import { swiper, swiperSlide } from 'vue-awesome-swiper'
export default {
name: 'imageGroup',
components: {
swiper,
swiperSlide
},
props: {
height: {
type: String,
default: '100px'
},
imgList: {
type: Array,
default: () => []
}
},
data () {
return {
swiperOptionTop: {
spaceBetween: 10
},
swiperOptionThumbs: {
spaceBetween: 10,
centeredSlides: true,
slidesPerView: 'auto',
touchRatio: 0.2,
slideToClickedSlide: true
},
isMounted: false
}
},
computed: {
swiperobj1 () {
if (!this.isMounted) return { index: 0 }
return this.$refs.swiperTop.swiper
},
swiperobj2 () {
if (!this.isMounted) return
return this.$refs.swiperThumbs.swiper
}
},
mounted () {
this.$nextTick(() => {
const swiperTop = this.$refs.swiperTop.swiper
const swiperThumbs = this.$refs.swiperThumbs.swiper
swiperTop.controller.control = swiperThumbs
swiperThumbs.controller.control = swiperTop
this.isMounted = true
})
},
methods: {
change (type) {
let activeIndex = this.swiperobj1.activeIndex
if (type === 'prev' && activeIndex !== 0) {
this.swiperobj1.slideTo(activeIndex - 1, 100)
this.swiperobj2.slideTo(activeIndex - 1, 100)
}
if (type === 'next' && activeIndex !== this.imgList.length) {
this.swiperobj1.slideTo(activeIndex + 1, 100)
this.swiperobj2.slideTo(activeIndex + 1, 100)
}
}
}
}
</script>
<style rel="stylesheet/scss" lang="scss" scoped>
.imageGroup, .swiperBox, .imgBox {
height: 100%;
}
.swiper-slide {
background-size: cover;
background-position: center;
}
.gallery-top {
height: 80%!important;
width: 100%;
}
.thumbsBox {
padding: 0 20px;
height: 20%!important;
position: relative;
}
.gallery-thumbs {
height: 100%!important;
box-sizing: border-box;
padding: 1px 0;
}
.gallery-thumbs .swiper-slide {
width: 25%;
height: 100%;
border: 1px solid #ccc;
box-sizing: border-box;
}
.gallery-thumbs .swiper-slide-active {
opacity: 1;
border: 1px solid #2D9AE9;
}
.prevBtn, .nextBtn {
color: #666;
position: absolute;
font-size: 30px;
font-weight: bold;
top: calc(50% - 15px);
}
.prevBtn {
left: -3px;
}
.nextBtn {
right: -3px;
}
.btnDisabled {
color: #ccc;
cursor: not-allowed;
}
.img {
width: 100%;
height: 100%;
}
</style>
效果图如下:
当前的缩略图在中间显示,这样看起来比较奇怪
这是修改后的代码
<template>
<div class="imageGroup clearfix">
<swiper :options="swiperOptionTop" class="gallery-top" ref="swiperTop">
<swiper-slide v-for="(item, index) in imgList" :key="index">
<img :src="item" class="img" />
</swiper-slide>
</swiper>
<!-- swiper2 Thumbs -->
<div class="thumbsBox">
<i @click="change('prev')" class="el-icon-arrow-left prevBtn" :class="{ btnDisabled: swiperobj1.activeIndex === 0 }"></i>
<swiper :options="swiperOptionThumbs" class="gallery-thumbs" ref="swiperThumbs">
<swiper-slide v-for="(item, index) in imgList" :key="index" :class="{ active: swiperobj1.activeIndex === index}">
<img @click="setIndex(index)" :src="item" class="img" />
</swiper-slide>
</swiper>
<i @click="change('next')" class="el-icon-arrow-right nextBtn" :class="{ btnDisabled: swiperobj1.activeIndex === imgList.length - 1 }"></i>
</div>
</div>
</template>
<script>
import 'swiper/dist/css/swiper.css'
import { swiper, swiperSlide } from 'vue-awesome-swiper'
export default {
name: 'imageGroup',
components: {
swiper,
swiperSlide
},
props: {
height: {
type: String,
default: '100px'
},
imgList: {
type: Array,
default: () => []
}
},
data () {
return {
swiperOptionTop: {
spaceBetween: 10
},
swiperOptionThumbs: {
spaceBetween: 10,
slidesPerView: 4,
freeMode: true,
watchSlidesVisibility: true,
watchSlidesProgress: true
},
isMounted: false
}
},
computed: {
swiperobj1 () {
if (!this.isMounted) return { index: 0 }
return this.$refs.swiperTop.swiper
},
swiperobj2 () {
if (!this.isMounted) return
return this.$refs.swiperThumbs.swiper
}
},
mounted () {
this.$nextTick(() => {
const swiperTop = this.$refs.swiperTop.swiper
const swiperThumbs = this.$refs.swiperThumbs.swiper
// swiperTop.controller.control = swiperThumbs
// swiperThumbs.controller.control = swiperTop
swiperTop.thumbs.swiper = swiperThumbs
this.isMounted = true
})
},
methods: {
change (type) {
let activeIndex = this.swiperobj1.activeIndex
if (type === 'prev' && activeIndex !== 0) {
this.swiperobj1.slideTo(activeIndex - 1, 100)
this.swiperobj2.slideTo(activeIndex - 1, 100)
}
if (type === 'next' && activeIndex !== this.imgList.length) {
this.swiperobj1.slideTo(activeIndex + 1, 100)
this.swiperobj2.slideTo(activeIndex + 1, 100)
}
},
setIndex (index) {
this.swiperobj1.slideTo(index, 100)
this.swiperobj2.slideTo(index, 100)
}
}
}
</script>
<style rel="stylesheet/scss" lang="scss" scoped>
.imageGroup, .swiperBox, .imgBox {
height: 100%;
}
.swiper-slide {
background-size: cover;
background-position: center;
}
.gallery-top {
height: 80%!important;
width: 100%;
}
.thumbsBox {
padding: 0 20px;
height: 20%!important;
position: relative;
}
.gallery-thumbs {
height: 100%!important;
box-sizing: border-box;
padding: 1px 0;
}
.gallery-thumbs .swiper-slide {
height: 100%;
border: 1px solid #ccc;
box-sizing: border-box;
}
// .gallery-thumbs .swiper-slide-active {
// border: 1px solid #2D9AE9;
// }
.gallery-thumbs .active {
border: 1px solid red;
}
.prevBtn, .nextBtn {
color: #666;
position: absolute;
font-size: 30px;
font-weight: bold;
top: calc(50% - 15px);
}
.prevBtn {
left: -3px;
}
.nextBtn {
right: -3px;
}
.btnDisabled {
color: #ccc;
cursor: not-allowed;
}
.img {
width: 100%;
height: 100%;
}
</style>
注意:第二个与第一个不一样的地方是,动态取当前轮播图的index然后和缩略图的index进行对比,相等就加上active这个类名
这里面还有一个知识点, 解决Vue computed计算属性中使用$refs进行dom操作时出现undefined问题
问题分析: computed中 return一个常量时不会再触发数据双向绑定,一开始$refs无效,此时computed属性返回了一个常量值,所以后续不在触发
解决办法 定义一个isMounted变量,并将其引入到计算属性property中。
export default {
data(){
return {
isMounted: false
}
},
computed:{
property(){
if(!this.isMounted)
return;
//$refs is available
}
},
mounted(){
this.isMounted = true;
}
}