// 调用组件
<List :arr="dataList">
<template #default="{item}">
{{item * 3}}
</template>
</List>
// 虚拟列表组件
<template>
<div class=''>
<!-- 给用户看到的20个元素的容器 -->
<div class="view_port" ref="viewPortRef" @scroll="changeScroll">
<!-- 为了撑开容器 -->
<div class="scroll_bar" ref="scrollBarRef"></div>
<!-- 虚拟的列表 -->
<div class="list" :style="{ transform: `translate(0, ${offsetTop}px)` }">
<div :style="{ 'height': size + 'px' }" v-for="(item, index) in itemsList" :key="index">
<slot :item="item">{{item}}</slot>
</div>
</div>
</div>
</div>
</template>
<script setup lang='ts'>
import { withDefaults, defineProps, ref, onMounted, computed } from "vue";
const props = withDefaults(defineProps<{
arr: Array<number>, // 需要滚动的数据
remain?: number, // 每个元素的高度
size?: number // 视口展示元素的数量
}>(), {
remain: 20,
size: 20
})
const viewPortRef = ref<HTMLElement>();
const scrollBarRef = ref<HTMLElement>();
const initPage = () => {
// 设置视口的高度
viewPortRef.value!.style.height = props.remain * props.size + "px";
// 设置整个滚动条的高度
scrollBarRef.value!.style.height = props.arr.length * props.size + 'px';
}
const startIndex = ref(0);
const endIndex = ref(props.size);
const offsetTop = ref(0);
// 用于获取真实得到的列表
const itemsList = computed(() => props.arr.slice(startIndex.value, endIndex.value))
// 监听页面的滚动
const changeScroll = () => {
// 计算向上卷入的元素的开始下标,乡下取整
startIndex.value = Math.floor(viewPortRef.value!.scrollTop / props.size);
// 计算截取的元素结束下标
endIndex.value = startIndex.value + props.size;
// 可视高度向下偏移的数量
offsetTop.value = startIndex.value * props.size
}
onMounted(() => {
initPage();
})
</script>
<style scoped>
.view_port {
overflow-y: scroll;
position: relative;
}
.list {
position: absolute;
top: 0;
left: 0;
}
</style>
标签:vue,const,value,列表,startIndex,虚拟,props,ref,size
From: https://www.cnblogs.com/rzl795/p/16988514.html