效果图:
安装swiper插件
创建Swiper.vue组件
<template>
<div class="swiper-component" @touchstart="onTouchStart" @touchend="onTouchEnd" :style="{height:height}">
<ul :style="[ulStyle, swiperStyle]">
<li v-for="(item, ind) in list" :key="ind" :style="{width: itemWidth + 'px'}" :class="[index===ind?'active':'', effect]" @click="handleClick(item)">
<img :src="item">
</li>
</ul>
<div v-show="options.showDots" class="swiper-dots">
<div v-for="(item, ind) in list" :key="ind" class="dots-item" :class="{active:index===ind}"></div>
</div>
</div>
</template>
<script>
export default {
name: "swiper",
data() {
return {
ulStyle: { width: "750px", paddingLeft: "20px" }, // 轮播图容器宽度
itemWidth: 750, // 单个轮播图容器的宽度,默认屏幕宽度,
swiperStyle: {}, // 控制轮播的样式
index: 0, // 当前显示的轮播图索引,默认第一张
touchStart: {}, // 触摸开始点位
touchEnd: {}, // 触摸结束点位
intervalTime: "" // 循环轮播定时器
};
},
props: {
height: {
type: String,
default: "3rem"
},
options: {
type: Object,
/*这里的default报错可以去掉,因为是在父组件里定义过*/
default: {
showDots: true, // 是否显示分页器
interval: 3000, // 轮播间隔时间,默认3s
autoplay: true, // 是否自动播放
loop: false // 是否循环轮播
}
},
list: {
type: Array,
/*这里的default报错可以去掉,因为是在父组件里定义过*/
default: []
},
effect: {
type: String,
default: "zoom" // 轮播图的样式类型,默认正常类型 normal,可选:zoom(缩放)
}
},
mounted() {
this.calcWidth();
this.handleLoopMove();
this.handleMove();
},
methods: {
/**
* 初始化时的一些宽度计算操作
*/
calcWidth() {
// 页面更新后执行宽度计算
this.$nextTick(function() {
if (this.effect === "normal") {
// 如果是正常模式,一张图的宽度为屏幕宽度
this.itemWidth = document.body.clientWidth; // 获取屏幕的宽度
} else if (this.effect === "zoom") {
// 如果是缩放模式,控制轮播图显示的宽度,两边流出空隙
this.itemWidth = document.body.clientWidth - 40; // 获取屏幕的宽度
}
this.handleType();
var length = this.list.length; // 获取列表的个数
this.ulStyle.width = parseInt((this.itemWidth + 40) * length) + "px"; // 容器总宽度
});
},
/**
* 轮播图点击事件
*/
handleClick(val) {
// 触发外部事件,将点击的轮播图详情数据返回
this.$emit("onClick", val);
},
/**
* 判断轮播类型,根据类型执行对应的操作
*/
handleType() {
if (this.effect === "normal") {
this.ulStyle.paddingLeft = 0; // 将起始位置左侧的padding置为0
} else if (this.effect === "zoom") {
this.ulStyle.paddingLeft = "20px"; // 保证左侧有一定的位移
}
},
/**
* 移动处理
*/
handleMove() {
let moveX = this.itemWidth * this.index;
if (this.index === 0) {
moveX = 0;
this.handleType();
} else {
this.ulStyle.paddingLeft = 0; // 将起始位置左侧的padding置为0
if (this.effect === "zoom") {
moveX = moveX - 20;
}
}
this.swiperStyle = {
transform: "translateX(-" + moveX + "px)"
};
},
/**
* 循环移动处理
*/
handleLoopMove() {
// 当设置自动播放时,执行自动循环播放操作,否则,只执行下一次轮播效果
if (this.options.autoplay) {
let interval = this.options.interval ? this.options.interval : 3000;
this.intervalTime = setInterval(() => {
this.index++;
if (this.index > this.list.length - 1) {
this.index = 0; // 置为-1,下次轮播时index就会变成0,图片定位到起始位置
}
this.handleMove();
}, interval);
}
},
/**
* 触摸开始事件,记录下触摸点
*/
onTouchStart(e) {
this.touchStart = e.changedTouches[0]; // 记录开始触摸点
// 清除定时器
clearInterval(this.intervalTime);
},
/**
* 触摸结束事件,记录下触摸点,比较当前点和触摸开始点,判断触摸方向
*/
onTouchEnd(e) {
this.touchEnd = e.changedTouches[0];
// 比较移动的点位差,正数就是右滑,负数就是左滑
if (this.touchEnd.clientX - this.touchStart.clientX > 60) {
this.index--;
if (this.index <= 0) {
this.index = 0;
}
} else if (this.touchEnd.clientX - this.touchStart.clientX < -60) {
this.index++;
if (this.index >= this.list.length - 1) {
this.index = this.list.length - 1;
}
}
// 处理当前的滑动
this.handleMove();
// 同时开启自动轮播
this.handleLoopMove();
}
},
watch: {
list: function(e) {
this.calcWidth();
}
},
destroyed() {
// 清除定时器
if (this.autoplay) {
clearInterval(this.intervalTime);
}
}
};
</script>
<style lang="less" scoped>
.swiper-component {
overflow: hidden;
height: 3rem;
position: relative;
ul {
white-space: nowrap;
height: 100%;
transition: 0.5s ease;
li {
list-style: none;
height: 100%;
float: left;
overflow: hidden;
img {
width: 100%;
height: 100%;
}
&.zoom {
border-radius: 0.16rem;
transform: scale(0.93);
transition: 0.5s ease;
&.active {
transform: scale(1);
}
}
}
}
.swiper-dots {
position: absolute;
bottom: 0.16rem;
left: 50%;
transform: translateX(-50%);
display: flex;
.dots-item {
width: 0.1rem;
height: 0.1rem;
border-radius: 50%;
background: rgba(255, 255, 255, 0.7);
margin: 0rem 0.04rem;
&.active {
background: #409eff;
}
}
}
}
</style>
父组件swiperdemo.vue
<template>
<div id="main">
<div class="nav-header">
<img @click="gohome" src="~@/assets/redeem-coupon/back.png" alt="" srcset="">
<span>会员权益</span>
<img id="nav-img-right" src="~@/assets/redeem-coupon/back.png" alt="" srcset="">
</div>
<div class="content">
<div class="swiperimgs" style="width:100%;">
<swiper @indexdata="indexdata" :vipCurrentProcess="vipCurrentProcess" height="3.4rem" :vipLevel="index" :options="swiperOption" :list="bannerList" effect="zoom" ></swiper>
</div>
<div class="grade-center" v-if="a.length > 0">
<p class="title">会员权益</p>
<div class="cart">
<div class="cart-data" v-for="(item,i) in a" :key="i" @click="popClick(item)">
<img :src="item.rightsIcon" />
<span>{{item.rightsName}}</span>
</div>
</div>
</div>
<div v-else class="grade-centert">
<div class="grade-center-title">会员权益</div>
<div class="grade-center-content">您目前暂无权益,继续努力哦~</div>
</div>
<div class="grade-footer">
<div class="title">VIP说明</div>
<div class="desc">{{desc}}</div>
</div>
</div>
<div class="footer"></div>
<div class="overlay">
<van-overlay :show="show" @click="show = false">
<div class="wrapper" @click.stop >
<div class="block" >
<div class="title-equity">
<img src="@/assets/home-vip/title_left.png">
<span>{{value.rightsName}}</span>
<img src="@/assets/home-vip/title_right.png">
</div>
<div class="title-desc">
{{value.rightsText}}
</div>
</div>
</div>
<div class="img-close" @click="closeClick">
<img src="@/assets/home-vip/closed_pop.png" alt="" srcset="">
</div>
</van-overlay>
</div>
</div>
</template>
<script>
import Swiper from "@/components/Swiper";
export default {
data() {
return {
eable: false,
swiperOption: {
showDots: false, // 是否显示分页器
interval: 3000, // 轮播间隔时间,默认3s
autoplay: false, // 是否自动播放
loop: true // 是否循环轮播
},
bannerList: [
{ img: require('../../assets/home-vip/gray.png') },
{ img: require('../../assets/home-vip/img_lv.png') },
{ img: require('../../assets/home-vip/img_huangjin.png') },
{ img: require('../../assets/home-vip/img_hong.png') },
{ img: require('../../assets/home-vip/img_cheng.png') },
{ img: require('../../assets/home-vip/img_lan.png') },
{ img: require('../../assets/home-vip/img_zi.png') }
],
}
},
components: {
Swiper
},
created() {
},
mounted() {
},
methods: {
//从子组件传递当前显示轮播图的索引
indexdata(data) {
this.index = data
const flag = this.vipDetailList && this.vipDetailList.length
this.a = flag ? this.vipDetailList[this.index].vipRightsList : []
this.desc = flag ? this.vipDetailList[this.index].growthValueText : ''
},
popClick(val) {
this.value = val
this.show = true
},
closeClick() {
this.show = false
}
}
}
</script>
<style lang="scss">
.block {
border-radius: 5px;
height: 180px;
position: absolute;
left: 37px;
right: 37px;
top: 248px;
background-image: url(../../assets/home-vip/bg_pop_yellow.png);
background-repeat: no-repeat;
background-size: 100% 100%;
> .title-equity {
display: flex;
align-items: center;
justify-content: center;
> img {
width: 30px;
height: 17px;
}
> span {
font-size:16px;
font-weight:bold;
color:rgba(95,48,32,1);
padding: 28px 10px;
}
}
> .title-desc {
padding: 0 17px;
font-size:15px;
color:rgba(148,98,57,1);
line-height:22px;
}
}
#main {
height: 100%;
background-color: #fafafa;
> .nav-header {
height: 40px;
background-color: #fff;
position: absolute;
top: 0;
left: 0;
right: 0;
display: flex;
align-items: center;
justify-content: space-between;
padding: 0 15px;
> span {
font-size:19px;
font-weight:bold;
color:rgba(0,0,0,1);
}
> img {
width: 21px;
height: 19px;
}
> #nav-img-right {
visibility: hidden;
}
}
> .content {
position: absolute;
top: 30px;
right: 0;
left: 0;
bottom: 40px;
overflow-y: scroll;
> .swiperimgs {
height: 3.3rem;
margin-top: 0.5rem;
}
> .grade-center {
background-color: #fff;
text-align: left;
padding-top: 25px;
> .title {
text-align: center;
width: 172px;
height: 18px;
margin: auto;
background-image: url(../../assets/home-vip/title1.png);
background-size: 100% 100%;
background-repeat: no-repeat;
font-size: 16px;
font-weight: bold;
margin-bottom: 20px;
}
> .cart {
display: flex;
align-items: center;
flex-wrap: wrap;
justify-content: space-around;
padding-bottom: 5px;
> .cart-data {
// width: 25%;
text-align: center;
display: flex;
align-items: center;
flex-direction: column;
> img {
width: 45px;
height: 45px;
}
> span {
font-size: 13px;
padding-top: 15px;
padding-bottom: 20px;
}
}
}
}
> .grade-centert {
text-align: center;
display: flex;
align-items: center;
flex-direction: column;
background-color: #fff;
.grade-center-title {
width: 172px;
height: 18px;
background-image: url(../../assets/home-vip/title1.png);
background-size: 100% 100%;
background-repeat: no-repeat;
font-size: 16px;
font-weight: bold;
margin-top: 25px;
margin-bottom: 20px;
}
.grade-center-content {
font-size:14px;
color:rgba(204,204,204,1);
margin-bottom: 24px;
}
}
> .grade-footer {
display: flex;
align-items: center;
flex-direction: column;
background-color: #fff;
margin-top: 10px;
> .title {
width: 172px;
height: 18px;
background-image: url(../../assets/home-vip/title1.png);
background-size: 100% 100%;
background-repeat: no-repeat;
font-size: 16px;
font-weight: bold;
margin-top: 25px;
margin-bottom: 20px;
}
> .desc {
padding-left: 15px;
padding-right: 28px;
font-size:14px;
color:rgba(51,51,51,1);
text-align: left;
padding-bottom: 25px;
}
}
}
> .footer {
height: 10px;
position: absolute;
bottom: 0;
}
> .overlay {
position: relative;
}
}
.img-close {
position: absolute;
text-align: center;
top: 459px;
left: 172px;
right: 172px;
}
.img-close img {
width: 30px;
height: 30px;
}
</style>