前言
最近在项目中遇到了轮播图的需求,以往都是直接上框架就完事了。但想想自己还是应该多练练手
思路
- 确立样式
- 父容器限制宽高,超出部分隐藏,设置为弹性布局
- 子容器利用
flex-shrink: 0保证容器不被缩小 - 加上左移,右移按钮和轮播图指示器
- 图片滚动
- 设置一个滚动变量
currentIndex(从0开始),表示当前展示的是第几张 - 设置
setInterval让滚动变量自增,并在生命周期函数中调用 - 设置一个计算属性,返回值为每次滚动的距离:(子容器的宽度+外边距)*滚动变量
- 设置一个滚动变量
transStyle(){
return `transform:translateX(${-this.currentIndex*this.itemWidth}px)`
}
- 无缝滚动
- 在滚动到最后一张图片时,会出现这样的尴尬情况
那么只要我将图片列表复制一份添加在在尾部,并在滚动变量
currentIndex==图片数量时,把currentIndex重置为0,同理,左移时如果滚动变量currentIndex==0,将currentIndex置为图片数量-1,这样就算无缝滚动啦。
html
<template>
<div class="swipe-wrap">
<div class="swipe">
<ul class="viewWrapper">
<li class="item" :style="transStyle" v-for="item in swipe" :key="item">
<img :src="item.pic_info.url" alt="" />
</li>
<li class="item" :style="transStyle" v-for="item in swipe" :key="item">
<img :src="item.pic_info.url" alt="" />
</li>
</ul>
<van-icon name="arrow-left" class="icon-left" @click="leftMove" />
<van-icon name="arrow" class="icon-right" @click="rightMove" />
<ul class="point-wrapper">
<li
v-for="(item, index) in swipe.length"
:key="item"
class="circle"
:class="index == currentIndex ? 'active' : ''"
@click="onHandle(index)"
></li>
</ul>
</div>
</div>
</template>
js
<script>
import { onMounted, reactive, toRefs } from "vue";
export default {
props: {
swipe: {
type: Array,
default() {
return [1, 2, 3, 4];
},
},
},
setup(props) {
const state = reactive({
currentIndex: 0, //滚动变量
autoTime: 2000, //自动播放间隔时长
wrapperWidth: 860, //整个父容器宽度
itemWidth: 430, //子容器宽度+外边距
});
const onHandle = (e) => {
state.currentIndex = e;
};
const leftMove = () => {
if (state.currentIndex == 0) {
state.currentIndex = props.swipe.length - 1;
return;
}
state.currentIndex--;
};
const rightMove = () => {
if (state.currentIndex >= props.swipe.length) return;
state.currentIndex++;
};
const autoMove = () => {
setInterval(() => {
state.currentIndex++;
if (state.currentIndex >= props.swipe.length) {
state.currentIndex = 0;
}
}, state.autoTime);
};
onMounted(() => {
autoMove();
});
return {
autoMove,
onHandle,
rightMove,
leftMove,
...toRefs(state),
};
},
computed: {
transStyle() {
return `transform:translateX(${-this.currentIndex * this.itemWidth}px)`;
},
},
};
</script>
css
<style lang="less" scoped>
.swipe-wrap {
position: relative;
.swipe {
width: 980px;
.icon-left {
display: none;
position: absolute;
font-size: 50px;
top: 31%;
left: -33px;
z-index: 1000;
}
.icon-right {
display: none;
position: absolute;
font-size: 50px;
top: 31%;
right: -10px;
}
i:hover {
display: block;
color: aquamarine;
}
.viewWrapper {
display: flex;
width: 860px;
overflow: hidden;
.item {
margin: 30px 15px;
width: 400px;
height: 200px;
flex-shrink: 0;
img {
width: 100%;
border-radius: 10px;
}
}
}
.point-wrapper {
position: absolute;
bottom: 12%;
left: 40%;
display: flex;
.circle {
width: 10px;
height: 10px;
border-radius: 50%;
margin-left: 20px;
background-color: #969799;
}
.active {
background-color: #111;
}
.circle:hover {
background-color: #07c1608c;
}
}
}
.swipe:hover {
i {
display: block;
}
}
}
</style>