vue3 版本:
<template>
<div class="device">
<div class="vir-scroll">
<div class="scroll-Y" @scroll="scroll">
<div class="parentDom">
<!-- 占位,根据数据条数生成滚动列表 -->
<div :style="{ height: data.screenHeight + 'px' }"></div>
<!-- 虚拟滚动列表 -->
<div
class="positionRelative"
:style="{ transform: data.getTransform }"
>
<div
class="scroll-item"
v-for="(item, index) in data.visibleData"
:key="index"
>
<div class="scroll-info">helloworld{{ item }}</div>
</div>
</div>
</div>
</div>
</div>
</div>
</template>
<script lang="ts" setup>
import { computed, reactive } from "vue";
const data = reactive({
dataList: Array(300000)
.fill(0)
.map((v, i) => `item-${i}`),
start: 0,
end: 10,
itemHeight: 143,
startOffset: 0,
visibleData: computed(() => {
return data.dataList.slice(
data.start,
Math.min(data.end, data.dataList.length)
);
}),
getTransform: computed(() => {
return `translate(0,${data.startOffset}px)`;
}),
screenHeight: computed(() => {
return data.dataList.length * data.itemHeight;
}),
});
const scroll = (e): void => {
scrollThrottle(e.target.scrollTop);
};
const scrollThrottle = (scrollTop): void => {
const topCount = Math.floor(scrollTop / data.itemHeight) - 8;
// 此时的开始索引
data.start = topCount >= 0 ? topCount : 0;
// 此时的结束索引
data.end = data.start + 25;
// 此时的偏移量
data.startOffset = data.start * data.itemHeight;
};
</script>
<style scoped>
.vir-scroll {
position: fixed;
left: 0;
width: 100%;
height: calc(100% - 196px);
padding: 0 16px;
z-index: 10;
border: 1px solid #ccc;
}
.scroll-Y {
width: 100%;
height: 100%;
overflow-y: auto;
}
.parentDom {
position: relative;
}
.positionRelative {
width: 100%;
position: absolute;
left: 0;
top: 0;
border-radius: 5px;
padding-bottom: 12.5px;
}
.scroll-item {
/* width: 100%; */
border-bottom: 1px solid #ccc;
height: 143px;
background-color: #ffffff;
font-size: 12px;
padding: 10px 15px 0px 15px;
}
</style>
vue2版本:
<template>
<div class="device">
<div class="vir-scroll">
<div class="scroll-Y" @scroll="scroll">
<div class="parentDom">
<!-- 占位,根据数据条数生成滚动列表 -->
<div :style="{ height: screenHeight + 'px' }"></div>
<!-- 虚拟滚动列表 -->
<div class="positionRelative" :style="{ transform: getTransform }">
<div class="scroll-item" v-for="item in visibleData" :key="item.index">
<div class="scroll-info">helloworld</div>
</div>
</div>
</div>
</div>
</div>
</div>
</template>
<script>
export default {
data() {
return {
/** 数据列表 */
dataList: [],
/** 单行高度 */
itemHeight: 143,
/** 偏移高度 */
startOffset: 0,
/** 起始显示数据 */
start: 0,
/** 结束显示数据 */
end: 10,
}
},
computed: {
/** 根据每条数据的高度获取总列表高度 */
screenHeight() {
return this.dataList.length * this.itemHeight
},
/** 前面预留 */
prevCount() {
return 8
},
/** 后面预留 */
nextCount() {
return 25
},
/** 每次截取虚拟列表的位置 */
getTransform() {
return `translate(0,${this.startOffset}px)`
},
/** 虚拟数据 */
visibleData() {
return this.dataList.slice(this.start, Math.min(this.end, this.dataList.length))
},
},
methods: {
/** 列表滚动,暂时不节流,因为滚动快触发次数就少,容易导致没有及时更新数组导致白屏 */
scroll(e) {
this.scrollThrottle(e.target.scrollTop)
},
/** 滚动函数 */
scrollThrottle(scrollTop) {
const topCount = Math.floor(scrollTop / this.itemHeight) - this.prevCount
// 此时的开始索引
this.start = topCount >= 0 ? topCount : 0
// 此时的结束索引
this.end = this.start + this.nextCount
// 此时的偏移量
this.startOffset = this.start * this.itemHeight
},
},
}
</script>
<style scoped lang="scss">
.device {
.vir-scroll {
// 脱离文档流避免回流
position: fixed;
left: 0;
width: 100%;
height: calc(100% - 196px);
padding: 0 16px;
z-index: 10;
.scroll-Y {
width: 100%;
height: 100%;
overflow-y: auto;
.parentDom {
position: relative;
.positionRelative {
width: 100%;
position: absolute;
left: 0;
top: 0;
border-radius: 5px;
padding-bottom: 12.5px;
.scroll-item {
height: 143px;
background-color: #ffffff;
font-size: 12px;
padding: 10px 15px 0px 15px;
}
}
}
}
}
}
</style>