vue3 自适应轮播看板+翻页【一次一页】

96 阅读1分钟

image.png

组件代码

<template>
	<div :class="['SwiperBoard', swiperUuid]">
		<article v-if="dataList.length" class="list_box" v-resize="onResize">
			<div class="hedaer_box" :style="{ width: boxWidth + 'px' }">
				<span class="title_box">{{ title }}</span>
				<span v-if="totalPages > 1" class="right_box">
					<icon-left @click="prev" :style="page === 1 ? 'color: #EFF0F1;' : ''" />
					{{ page }}/{{ totalPages }}
					<icon-right @click="next" :style="page === totalPages ? 'color: #EFF0F1;' : ''" />
				</span>
			</div>
			<div class="swiper_box" :style="{ width: boxWidth + 'px' }">
				<template v-for="(item, index) in dataList">
					<div :class="['swiper_item', 'swiper_item' + index]" :style="`padding:${cardPadding};`">
						<slot :record="item" :index="index"></slot>
					</div>
				</template>
			</div>
		</article>
	</div>
</template>

<script setup>
import { ref, computed, watch, nextTick } from "vue";

// 组件实例的唯一标识
const swiperUuid = "id" + uuid();

/**
 * 注意 【 间距采用 卡片 cardPadding 调整 】
 *  */
const props = defineProps({
	dataList: {
		type: Array,
		default: [],
	},
	title: {
		type: String,
	},
	cardPadding: {
		type: String,
		default: "10px",
	},
});

watch(
	() => props.dataList,
	(val) => {
		if (val.length) {
			nextTick(() => {
				itemWidth.value = document.querySelector(`.${swiperUuid} .swiper_item`).clientWidth;
			});
		}
	},
	{ deep: true, immediate: true }
);

const curIndex = ref(0);
const itemWidth = ref(0);
const showItemLength = ref(5);

const boxWidth = computed(() => {
	return itemWidth.value * showItemLength.value;
});
const page = computed(() => {
	return Math.ceil(curIndex.value / showItemLength.value) + 1;
});
const totalPages = computed(() => {
	return Math.ceil(props.dataList.length / showItemLength.value);
});

const next = () => {
	let index =
		curIndex.value + showItemLength.value > props.dataList.length - 1 - showItemLength.value
			? props.dataList.length - 1 - showItemLength.value
			: curIndex.value + showItemLength.value;
	const curSwiperItem = document.querySelector(`.${swiperUuid} .swiper_item${index}`);
	if (curSwiperItem) {
		curIndex.value = index;
		curSwiperItem.scrollIntoView({
			behavior: "smooth",
			block: "nearest",
			inline: "start",
		});
	}
};
const prev = () => {
	let index = curIndex.value - showItemLength.value < 0 ? 0 : curIndex.value - showItemLength.value;
	const curSwiperItem = document.querySelector(`.${swiperUuid} .swiper_item${index}`);
	if (curSwiperItem) {
		curIndex.value = index;
		curSwiperItem.scrollIntoView({
			behavior: "smooth",
			block: "nearest",
			inline: "start",
		});
	}
};
const onResize = (el) => {
	showItemLength.value = Math.floor(el.clientWidth / itemWidth.value);
};

function uuid() {
	return "xxxxxxxx".replace(/[xy]/g, function (c) {
		var r = (Math.random() * 16) | 0,
			v = c == "x" ? r : (r & 0x3) | 0x8;
		return v.toString(16);
	});
}
</script>

<style lang="scss" scoped>
.SwiperBoard {
	margin: 15px 0;
	.hedaer_box {
		margin: 0 auto;
		padding: 0 10px;
		box-sizing: border-box;
		display: flex;
		justify-content: space-between;

		align-items: center;
		.title_box {
			font-size: 28px;
			font-weight: 600;
			color: #1c1c1d;
		}
		.right_box {
			display: flex;
			align-items: center;
			color: #7f7f82;
			user-select: none;
		}
	}
	.swiper_box {
		display: flex;
		white-space: nowrap;
		overflow-x: auto;
		margin: 0 auto;
		&::-webkit-scrollbar {
			height: 0;
		}
		.swiper_item {
			display: inline-block;
		}
		:last-child {
			margin-right: 0;
		}
	}
}
</style>

配合 v-resize 自定义组件完成自适应

export const useDirective = (app) => {
	// 注册自定义指令 v-resize
	app.directive("resize", {
		bind(el, binding) {
			// el为绑定的元素,binding 为绑定给指令的对象
			let width = "";
			let height = "";
			function isReize() {
				const style = document.defaultView.getComputedStyle(el);
				if (width !== style.width || height !== style.height) {
					binding.value(el);
				}
				width = style.width;
				height = style.height;
			}
			el.__vueSetInterval__ = setInterval(isReize, 300);
		},
		unbind(el) {
			clearInterval(el.__vueSetInterval__);
		},
	});
};

导入到 main.js 里挂载

使用

<SwiperBoard :dataList="dataList" :title="scoped.name" cardPadding="16px 10px">
	<template #default="{ record, index }">
		<div class="card_wrap">
			// 自定义卡片
		</div>
	</template>
</SwiperBoard>