超好用的轮播插件vue-awesome-swiper@3,双向控制实现自定义分页器和前后指示器。解决动态获取数据时,错乱不稳定的bug
一、需求、demo效果图
1、需求描述
- 将一个链接列表,嵌套在iframe里自动轮播播放。底部放链接的菜单名,点击即跳转轮播至指定页面。
- 由于链接的数量较多,所以分页指示器需要把当前命中的按钮居中,其他多余的按钮隐藏。
2、demo效果图
二、vue-awesome-swiper
无意间发现vue-awesome-swiper
这个宝藏轮播插件,真是太好用啦,功能强大,自定义也非常灵活。
1、官网地址、组件安装
Swiper官网中文文档地址:https://www.swiper.com.cn/api/index.html
组件安装
我用的版本是[email protected]
,适用于vue2。vue3的要用更高版本。所以安装版本一定要注意。
npm install vue-awesome-swiper@3.1.3 --save
2、思路详解
- 因为要自定义分页器,一开始考虑用swiper的
pagination
属性,采用renderBullet
方法自定义分页器。结果当然是可行的。 - 但是又因为我要隐藏多余的分页器指示点,所以又用了
dynamicBullets
+dynamicMainBullets
设置动态指示点。但是出来的效果不理想,因为首次渲染时,左边会没有指示点。如图所示:
- 我想要始终都是图二的效果,所以采用
pagination
自定义分页器的思路就有点行不通了。(可能renderCustom
方法可行,但是不想考虑得太复杂,因为以下的方法实现更快) - 看到官网的缩略图双向控制的demo后,豁然开朗,要的就是那个效果。
- 最终思路:实现两个swiper,一个作为内容swiper,一个作为自定义分页器swiper。两个swiper设置为互相控制即可。
三、demo代码
1、html
<template>
<div class="page-container">
<!-- iframe轮播页面 -->
<div>
<!-- 页面轮播器 -->
<swiper ref="pageSwiper" :options="pageSwiperOptions">
<swiper-slide
v-for="(item, index) in menuList"
:key="index">
<iframe :src="item.url" width="100%" frameborder="0" border="0" class="iframe"></iframe>
</swiper-slide>
</swiper>
<!-- 菜单轮播器 -->
<swiper ref="navSwiper" :options="navSwiperOptions" class="nav-swiper">
<swiper-slide
v-for="(item, index) in menuList"
:key="index"
class="menu-item">
<span>{
{item.title}}</span>
<div class="active-img"></div>
</swiper-slide>
</swiper>
<!-- 前进后退按钮 -->
<div class="btn-nav btn-nav-prev"></div>
<div class="btn-nav btn-nav-next"></div>
</div>
</div>
</template>
2、js
<script>
import 'swiper/dist/css/swiper.css'
import {
swiper, swiperSlide } from 'vue-awesome-swiper'
export default {
name: "Home",
components: {
swiper,
swiperSlide
},
data() {
return {
// 菜单列表
menuList: [
{
title: '菜单1',
url: 'https://www.baidu.com'
},
{
title: '菜单2',
url: 'https://www.baidu.com'
},
{
title: '菜单3',
url: 'https://www.baidu.com'
},
{
title: '菜单4',
url: 'https://www.baidu.com'
},
{
title: '菜单5',
url: 'https://www.baidu.com'
},
{
title: '菜单6',
url: 'https://www.baidu.com'
},
{
title: '菜单7',
url: 'https://www.baidu.com'
},
{
title: '菜单8',
url: 'https://www.baidu.com'
},
{
title: '菜单9',
url: 'https://www.baidu.com'
},
{
title: '菜单10',
url: 'https://www.baidu.com'
},
{
title: '菜单11',
url: 'https://www.baidu.com'
},
{
title: '菜单12',
url: 'https://www.baidu.com'
}
],
// 页面轮播器配置项
pageSwiperOptions: {
// 自动切换
autoplay: {
// 停留时间
delay: 30000,
// 用户操作swiper之后,是否禁止autoplay
disableOnInteraction: false,
},
// 环路
loop: true,
// 两个swiper的loopedSlides要相同
loopedSlides: 5,
// 前进后退按钮
navigation: {
nextEl: '.btn-nav-next',
prevEl: '.btn-nav-prev',
},
},
// 菜单轮播器配置项
navSwiperOptions: {
// 环路
loop: true,
// 两个swiper的loopedSlides要相同
loopedSlides: 5,
// 在slide之间设置距离(单位px)
spaceBetween: 25,
// 居中幻灯片。设定为true时,当前的active slide 会居中,而不是默认状态下的居左。
centeredSlides: true,
// 设置slider容器能够同时显示的slides数量
slidesPerView: 7,
// 触摸比例。触摸距离与slide滑动距离的比率。
touchRatio: 0.2,
// 设置为true则点击slide会过渡到这个slide
slideToClickedSlide: true
}
}
},
mounted() {
// 设置两个轮播器相互控制
this.$nextTick(() => {
const pageSwiper = this.$refs.pageSwiper.swiper
const navSwiper = this.$refs.navSwiper.swiper
pageSwiper.controller.control = navSwiper
navSwiper.controller.control = pageSwiper
})
}
};
</script>
3、css
<style rel="stylesheet/scss" lang="scss">
.page-container {
min-height: 100vh;
}
/*轮播*/
.swiper-slide{
background-color: #cadeff;
}
.iframe {
display: block;
height: calc(100vh - 80px);
}
/*底部菜单分页器样式*/
.menu-item{
color: #1353ff;
height: 50px;
line-height: 50px;
text-align: center;
white-space: nowrap;
border-radius: 10px;
font-size: 16px;
cursor: pointer;
transition-duration: 0.3s;
position: relative;
&:hover {
transform: scale(1.1);
}
.active-img{
visibility: hidden;
width: 130%;
height: 120px;
position: absolute;
left: -15%;
bottom: -58px;
background-image: url("../assets/images/active.png");
background-size: 100% 100%;
}
}
.nav-swiper{
width: 70%;
padding: 0 10px;
.swiper-wrapper{
padding: 15px 0;
}
/*菜单命中样式*/
.swiper-slide-active{
color: #fff;
background-color: #257cf4 !important;
.active-img{
visibility: visible;
}
}
}
/*前进后退指示器*/
.btn-nav{
width: 76px;
height: 28px;
background-size: 100% 100%;
background-repeat: no-repeat;
cursor: pointer;
transition-duration: 0.3s;
position: fixed;
bottom: 26px;
z-index: 99;
&:hover {
transform: scale(1.1);
}
&:active {
transform: scale(0.9);
}
&.btn-nav-prev{
background-image: url("../assets/images/btn-prev.png");
left: 30px;
}
&.btn-nav-next{
background-image: url("../assets/images/btn-next.png");
right: 30px;
}
}
</style>
四、解决动态获取数据时组件错乱不稳定的bug
如果你的swiper内容是静态的,不需要从后端接口动态获取数据的话,那么以上步骤就结束了。
但如果swiper内容是从后端动态获取
的数据的话,就有可能会出现组件错乱、不稳定的情况。(本来自信满满,一发布直接踩泥坑里。。。)
解决方法如下:
1、在swiper组件上,添加
v-if
,让swiper组件在获取到数据后才开始渲染
2、js异步获取数据后,再设置两个轮播器互相控制
修改后的代码如下:
html:
<template>
<div class="page-container">
<!-- iframe轮播页面 -->
<div>
<!-- 页面轮播器 -->
<swiper v-if="menuList.length>0" ref="pageSwiper" :options="pageSwiperOptions">
<swiper-slide
v-for="(item, index) in menuList"
:key="index">
<iframe :src="item.url" width="100%" frameborder="0" border="0" class="iframe"></iframe>
</swiper-slide>
</swiper>
<!-- 菜单轮播器 -->
<swiper v-if="menuList.length>0" ref="navSwiper" :options="navSwiperOptions" class="nav-swiper">
<swiper-slide
v-for="(item, index) in menuList"
:key="index"
class="menu-item">
<span>{
{item.title}}</span>
<div class="active-img"></div>
</swiper-slide>
</swiper>
<!-- 前进后退按钮 -->
<div class="btn-nav btn-nav-prev"></div>
<div class="btn-nav btn-nav-next"></div>
</div>
</div>
</template>
js:
<script>
import 'swiper/dist/css/swiper.css'
import {
swiper, swiperSlide } from 'vue-awesome-swiper'
export default {
name: "Home",
components: {
swiper,
swiperSlide
},
data() {
return {
// 菜单列表
menuList: [],
// 页面轮播器配置项
pageSwiperOptions: {
// 自动切换
autoplay: {
// 停留时间
delay: 30000,
// 用户操作swiper之后,是否禁止autoplay
disableOnInteraction: false,
},
// 环路
loop: true,
// 两个swiper的loopedSlides要相同
loopedSlides: 5,
// 前进后退按钮
navigation: {
nextEl: '.btn-nav-next',
prevEl: '.btn-nav-prev',
},
},
// 菜单轮播器配置项
navSwiperOptions: {
// 环路
loop: true,
// 两个swiper的loopedSlides要相同
loopedSlides: 5,
// 在slide之间设置距离(单位px)
spaceBetween: 25,
// 居中幻灯片。设定为true时,当前的active slide 会居中,而不是默认状态下的居左。
centeredSlides: true,
// 设置slider容器能够同时显示的slides数量
slidesPerView: 7,
// 触摸比例。触摸距离与slide滑动距离的比率。
touchRatio: 0.2,
// 设置为true则点击slide会过渡到这个slide
slideToClickedSlide: true
}
}
},
created() {
this.getList();
},
methods: {
getList(){
let that = this;
getScreenMenu().then(res=>{
this.menuList = res.data;
// 设置两个轮播器互相控制
this.$nextTick(() => {
const pageSwiper = this.$refs.pageSwiper.swiper
const navSwiper = this.$refs.navSwiper.swiper
pageSwiper.controller.control = navSwiper
navSwiper.controller.control = pageSwiper
})
})
}
}
};
</script>
由此动态获取数据的情况,渲染效果和静态数据渲染效果就会一模一样啦~~~