基本思路:需要两个容器,外层容器高度固定,超出后滚动。每条数据高度固定,内层容器的高度=每条数据高度数据条数,每页显示条数=外层容器高度/每条数据高度,监听外层容器的滚动事件,根据scrollTop来确定可视区域内起止数据在总数据的索引值,每条数据对应的元素设置绝对定位,top=索引值每条数据高度。为防止滑动过快产生空白,需要设置缓冲条数。
<template>
<div id="outerBox" @scroll="onScroll" :style="{height:`${outerBoxHeight}px`,width:'200px',border:`1px solid #000`,overflow:'auto'}">
<ul id="innerBox" :style="{position:'relative',height:`${liHeight*list.length}px`}">
<!-- 只展示startIndex和endIndex之间的数据 -->
<li v-for="(item,index) in list" v-if="index>=startIndex && index<=endIndex" :style="{position:'absolute',top:`${index*liHeight}px`,left:0,width:'100%',height:`${liHeight}px`,lineHeight:`${liHeight}px`}">{{item.title}}</li>
</ul>
</div>
</template>
<script>
export default{
data() {
return {
list:[],//列表
size:0,//可视区域显示条数
liHeight:30,//每条数据高度,假设是30
bufferSize:20,//缓冲条数
outerBoxHeight:300,//外层容器高度,假设是300
startIndex:0,//可视区域起始数据索引
endIndex:0, //可视区域截止数据索引
}
},
mounted() {
//假设有10000条数据
for(let i=0;i<10000;i++){
this.list.push({title:'数据-'+(i+1)})
}
// 每页显示条数=外层容器高度/每条数据高度
this.size=Math.round(this.outerBoxHeight/this.liHeight)
},
methods: {
onScroll(e){
//滚动高度
const {scrollTop}=e.target
//当前第一条数据索引值=滚动高度/每条数据高度
const currentIndex=Math.floor(scrollTop/this.liHeight)
//可视区域起始数据索引 从0和当前索引-缓冲条数中取大值
this.startIndex=Math.max(0,currentIndex-this.bufferSize)
//可视区域截止数据索引 从最大索引和当前索引+缓冲条数中取小值
this.endIndex=Math.min(this.list.length-1,currentIndex+this.bufferSize)
}
},
}
</script>