<template>
<div class="virtual-list-container" ref="container" @scroll="onScroll">
<div class="infinite-list-phantom" :style="{ height: listHeight + 'px' }"></div>
<div class="virtual-list" :style="{ transform: getTransform }">
<div class="virtual-list-item" v-for="(item, index) in visibleData" :key="index"
:style="{ height: itemSize + 'px', lineHeight: itemSize + 'px' }">
{{ item }}
</div>
</div>
</div>
</template>
<script setup lang="ts">
import { ref, computed, toRefs, onMounted, nextTick } from "vue";
const screenHeight = ref(0);
const startIndex = ref(0);
const endIndex = ref(0);
const startOffset = ref(0);
onMounted(() => {
screenHeight.value = container.value.clientHeight;
startIndex.value = 0;
endIndex.value = startIndex.value + visibleCount.value;
startOffset.value = startIndex.value * itemSize.value;
});
const props = defineProps({
listData: {
type: Array<any>,
default: () => [],
},
itemSize: {
type: Number,
defualt: 60,
},
});
const { listData, itemSize } = toRefs(props);
const container = ref();
const listHeight = computed(() => listData.value.length * itemSize.value);
const visibleCount = computed(() =>
Math.ceil(screenHeight.value / itemSize.value)
);
const getTransform = computed(
() => `translate3d(0, ${startOffset.value}px, 0)`
);
const visibleData = computed(() =>
listData.value.slice(startIndex.value, endIndex.value)
);
const onScroll = (event: Event) => {
let scrollTop = container.value.scrollTop;
startIndex.value = Math.floor(scrollTop / itemSize.value);
endIndex.value = startIndex.value + visibleCount.value;
startOffset.value = scrollTop - (scrollTop % itemSize.value);
// console.log("startIndex", startIndex.value, "endIndex", endIndex.value);\
nextTick(() => {
console.log("visibleData", visibleData.value);
});
};
</script>
<style scoped>
.virtual-list-container {
width: 300px;
height: 600px;
overflow: auto;
position: relative;
-webkit-overflow-scrolling: touch;
}
.infinite-list-phantom {
position: absolute;
left: 0;
top: 0;
right: 0;
z-index: -1;
}
.virtual-list {
left: 0;
right: 0;
top: 0;
position: absolute;
text-align: center;
}
.virtual-list-item {
padding: 10px;
color: #555;
box-sizing: border-box;
border-bottom: 1px solid #999;
width: 100px;
}
</style>