默认视图区域的top值为0,监听滚动事件,当滚动时计算当前可视区域的开始结束下标,实时改变top值以及该区域内渲染的数据,从而实现了虚拟列表
<template>
<div class='wrap' ref="viewport" @scorll="handleScroll">
// 定位的top是动态的,父组件的scrollTop值
<div class='list' :style="{top: topVal + 'px'}">
<div
v-for="item in showListData"
:key="item.id"
class="item"
>
{{ item.key }}
</div>
</div>
</div>
</template>
<script lang ='ts' setup>
import { ref, computed, onMounted } from 'vue'
type Item = {
id: number
key: string
}
const allListData = ref<Item[]>([]) // 所有的列表数据
const itemHeight = ref(40) // 每一条的高度
const count = ref(10) // 可视区域展示几条数据
const startIndex = ref(0) // 开始位置的索引
const endIndex = ref(10) // 结束位置的索引
const topVal = ref(0) // 父元素滚动高度
// 获取列表所有的数据
const getData = () => {
for (let i = 0; i < 10000; i++) {
allListData.value.push({key: `第${i}条`, id: i})
}
}
// 计算可视区域的列表数据
const showListData = computed(() => {allListData.value.slice(startIndex.value, endIndex.value)})
// 虚拟列表视口区域的组件实例
const viewport = ref<HTMLDivElement>()
const handleScroll = () => {
// 非空判断
if(!viewport.value) return
const scrollTop = viewport.value.scrollTop // 获取滚动距离
// 获取当前可视区域的开始下标
startIndex.value = Math.floor(scrollTop / itemHeight.value)
// 获取当前可视区域的结束下标
endIndex.value = Math.floor(startIndex.value + count.value )
// 动态更改定位的 top 值,动态展示相应内容
topVal.value = viewport.value.scrollTop
}
// 初始化加载
onMounted(() => {
getData()
})
</script>
<style scoped lang="scss">
.wrap{
box-sizing: border-box;
width: 200px;
overflow-y: auto;
position: relative;
.list{
width: 100%;
top: 0;
left: 0;
position: absolute;
.item {
line-height: 40px;
text-align: center;
height: 40px;
}
}
}
</style>