vue3版虚拟滚动
2023-05-15 本文已影响0人
一只小vivi
案例
话不多说直接代码见
父组件模版
<template>
<div
class="scroll-list"
:style="`height:${viewH}px;overflow-y: auto;`"
@scroll="scrollHandler"
>
<div class="card-content" :style="`height:${scrollH}px;`">
<div class="card_box" :style="`transform:translateY(${offsetY}px)`">
<Card
v-for="(item, index) in showList"
:key="index"
:itemSource="item"
:index="index"
></Card>
</div>
</div>
</div>
</template>
父组件上的逻辑代码
<script setup>
import Card from "@/components/scrollList/card.vue"; // 引入子组件
import { pageQueryCompetitor } from "@/api/index.js"; // 后端接口引入
import { onMounted, ref } from "vue";
const sourceList = ref([]); // 所有数据
const showList = ref([]); // 页面实际展示
const viewH = ref(500); // 外层容器高度
const itemH = ref(70); // 单个高度
const scrollH = ref(0); // 滚动容器高度
const showNum = ref(0); // 展示几个
const lastTime = ref(0);
const offsetY = ref(0); // 滚动偏移量
const current = ref(1);
const isLoading = ref(false); // 是否接口还在加载中
const isFail = ref(false); // 请求是否失败
const isEnd = ref(false); // 数据是否加载完了
const scrollHandler = async (e) => {
let newTime = new Date().getTime();
let bottomH = itemH.value * showNum.value * 2;
if (newTime - lastTime.value > 10) {
let scrollTop = e.target.scrollTop;
// 以1屏为基准进行偏移
offsetY.value = scrollTop - (scrollTop % (itemH.value * showNum.value));
// 触底判断
let bool = e.target.scrollHeight - viewH.value - scrollTop > bottomH;
if (!bool) {
getSliceHandler(scrollTop);
if (isLoading.value) return;
if (!isFail.value && !isEnd.value) {
current.value++;
}
isLoading.value = true;
await getHttpHandler();
} else {
getSliceHandler(scrollTop);
}
}
};
// 展示内容进行截取
const getSliceHandler = (scrollTop) => {
let index = Math.floor(scrollTop / (itemH.value * showNum.value));
showList.value = sourceList.value.slice(
index * showNum.value,
index * showNum.value + showNum.value * 2
);
};
// 获取数据
const getHttpHandler = async () => {
// 传給后端的入参
let params = {
current: current.value,
eid: "4c8bd41b-2534-43c0-8643-5bf4dca1991e",
pageSize: 30,
queryType: 0,
sortType: 0,
};
try {
const { success, data } = await pageQueryCompetitor(params);
if (success) {
isFail.value = false;
if (!data.records.length) {
isEnd.value = true;
return;
}
sourceList.value.push(...data.records);
scrollH.value = sourceList.value.length * itemH.value;
lastTime.value = new Date().getTime();
isLoading.value = false;
}
} catch (e) {
isFail.value = true;
isLoading.value = false;
}
};
onMounted(async () => {
showNum.value = Math.floor(viewH.value / itemH.value) + 1;
await getHttpHandler();
showList.value = sourceList.value.slice(0, showNum.value * 2);
});
</script>
子组件模版
<template>
<div class="card">
<div class="item">{{ itemSource.competitor_name }}{{ index + 1 }}</div>
</div>
</template>
<script setup>
defineProps({
itemSource: {
type: Object,
default: {},
},
index: {
type: Number,
default: 0,
},
});
</script>
<style lang="scss" scoped>
.card {
padding-bottom: 10px;
.item {
width: 400px;
height: 60px;
background-color: #008c8c;
font-size: 14px;
}
}
</style>