Open varHarrie opened 8 months ago
<template> <div class="list" ref="listRef" @scroll="onScroll"> <div class="inner" :style="{ height: innerHeight }"></div> <div class="item" v-for="item of visibleList" :style="{ top: item.top }">{{ item.value }}</div> </div> </template> <script setup> import { ref, reactive, computed, onMounted } from 'vue' const itemHeight = 50; const bufferSize = 2; const list = Array.from({length: 10000}).map((_, i) => i); const listRef = ref(); const listMeta = reactive({ offsetHeight: 0, scrollTop: 0 }); const onScroll = () => { listMeta.offsetHeight = listRef.value.offsetHeight; listMeta.scrollTop = listRef.value.scrollTop; } onMounted(() => { if (listRef.value) onScroll(); }) const innerHeight = computed(() => itemHeight * list.length + 'px'); const visibleList = computed(() => { const result = []; const { scrollTop, offsetHeight } = listMeta; const startIndex = Math.floor(scrollTop / itemHeight); const endIndex = Math.ceil((scrollTop + offsetHeight) / itemHeight); const finalStartIndex = Math.max(0, startIndex - bufferSize); const finalEndIndex = Math.min(list.length - 1, endIndex + bufferSize); for (let i = finalStartIndex; i < finalEndIndex; i++) { result.push({ top: i * itemHeight + 'px', value: list[i] }) } return result; }); </script> <style> .list { position: relative; height: 500px; width: 300px; border: 2px solid #000; overflow-y: scroll; .item { position: absolute; display: flex; align-items: center; justify-content: center; width: 100%; box-sizing: border-box; height: 50px; border: 1px solid #000; } } </style>