效果图
<template><view class="swiper-container" ref="root" @touchstart="onTouchStart" @touchend="onTouchEnd"><view @click="evtChangeIndex(index)" class="side" v-for="(item, index) in state.source" :key="index":style="item.sty" :class="item.className"><img :src="item?.img" alt=""></view></view>
</template><script>import { ref, computed, reactive, watch, onMounted } from 'vue';export default {props: {/*** 轮播数据来源*/source: {type: Array,default: () => [{ img: require('@/assets/imgs/test/1.png') },{ img: require('@/assets/imgs/test/1.png') },{ img: require('@/assets/imgs/test/1.png') },{ img: require('@/assets/imgs/test/1.png') },{ img: require('@/assets/imgs/test/1.png') },],},/*** 初始激活的索引*/initial: {type: Boolean,default: 0},/*** 自动播放时间*/interval: {type: Number,default: 3500},},emits: ['update:active'],setup(props, { emit }) {/*** 轮播数据源*/let source = props.source;/*** 轮播数据源长度差*/let diff = source.length - 5;// 不足5个数据,补全if (diff < 0) {//缺几个补几个diff = Math.abs(diff);source.slice(0, diff).forEach(item => {source.push(item);})}/*** 处理每一项的样式* @param {*} initial 当前项 * @param {*} source 数据源*/const computed = (initial, source) => {// 当前项的索引let len = source.length;//先处理中间值initial = initial < 0 ? 0 : (initial >= len ? len - 1 : initial);//5个项的templet temp1 = initial - 2;let temp2 = initial - 1;let temp3 = initial;let temp4 = initial + 1;let temp5 = initial + 2;//处理边界值temp1 = temp1 < 0 ? len + temp1 : temp1;temp2 = temp2 < 0 ? len + temp2 : temp2;//超界行为temp4 = temp4 >= len ? temp4 - len : temp4;temp5 = temp5 >= len ? temp5 - len : temp5;//计算每一项样式return source.map((item, index) => {let transform = 'translate(-50%, -50%) scale(0.55)',zIndex = 0,className = 'side';switch (index) {//中间的case temp3:zIndex = 3;className = 'side active';transform = 'translate(-50%, -50%) scale(1)';break;case temp1:zIndex = 1;className = 'side active';transform = 'translate(-160%, -50%) scale(0.8)';break;case temp2:zIndex = 2;className = 'side active';transform = 'translate(-110%, -50%) scale(0.9)';break;case temp4:zIndex = 2;className = 'side active';transform = 'translate(10%, -50%) scale(0.9)';break;case temp5:zIndex = 1;className = 'side active';transform = 'translate(60%, -50%) scale(0.8)';break;}item.sty = {zIndex,transform}item.className = className;return item;})}source = computed(props.initial, source);/*** 构建响应式*/const state = reactive({initial: props.initial,source})let autoTimer = null;/*** 监听激活数据变化*/watch(() => state.initial, (initial, preInitial) => {state.source = computed(initial, state.source);})/*** 自动轮播*/const autoPlay = () => {autoTimer = setInterval(() => {state.initial++;//边界处理if (state.initial >= state.source.length) {state.initial = 0;}}, props.interval);}/*** 手势处理逻辑*/let startX = 0;let endX = 0;const onTouchStart = (e) => {startX = e.touches[0].clientX;//手势关闭自动滚动stopAutoPlay();};const onTouchEnd = (e) => {endX = e.changedTouches[0].clientX;if (startX > endX + 50) {// 左滑change('right');} else if (startX < endX - 50) {// 右滑change('left');}//手势识别后重启autoPlay();};/*** 停止自动播放*/const stopAutoPlay = () => {clearInterval(autoTimer);}let root = ref(null);//开启自动轮播onMounted(() => {//开启自动轮播autoPlay();let box = root.value;// box.onmouseenter = function () {// clearInterval(autoPlay);// }// box.onmouseleave = function () {// autoPlay();// }})/*** 切换* @param dir left:上一张,right:下一张*/const change = (dir) => {if (dir == 'left') {state.initial--;if (state.initial < 0) {state.initial = state.source.length - 1;}}if (dir == 'right') {state.initial++;//超出边界定向到0if (state.initial >= state.source.length) {state.initial = 0;}}}/*** 轮播切换 具体值*/const evtChangeIndex = (index) => {state.initial = index;//重启定时stopAutoPlay();autoPlay();}return {state,root,change,evtChangeIndex,onTouchStart,onTouchEnd}},
};
</script><style lang="scss">
.swiper-container {width: 360px;display: flex;height: 200px;justify-content: center;/* 水平方向居中 */align-items: center;/* 垂直方向居中 */position: relative;background: transparent;overflow: hidden;/* 隐藏超出部分 */.side {position: absolute;width: 35%;height: 200px;top: 50%;left: 50%;transform: translate(-50%, -50%) scale(1);transition: 0.5s linear;border-radius: 3px;overflow: hidden;img {height: 100%;width: 100%;}}
}
</style>