实现思路
- 确定一个可视化的高度
- 确定每个元素的高度
- 由以上两点计算出可视化区域内元素的个数
- 发生滚动的时候,滚动的距离可以确定可视化区域中第一个元素的index
- 由以上三点可以确定滚动时,可视化区域中最后一个元素的Index
- 把对应数据中这两个index之间的数据放进渲染列表中
<template>
<div class="infinite-list-container" ref="list" @scroll="scrollEvent">
<div class="infinite-list-phantom" :style="listHeight + 'px'">
</div>
<div class="infinite-list" :style="{transform: getTransform}">
<div class="infinite-list-item" v-for="item in visibleData" :key="item.id" :style="{ height: itemSize + 'px', lineHeight: itemSize + 'px' }">
<div class="left-section">
{{item.title[0]}}
</div>
<div class="right-section">
<div class="title">
{{item.title}}
</div>
<div class="desc">
{{item.content}}
</div>
</div>
</div>
</div>
</div>
</template>
<script lang="ts" setup>
import { ref, computed } from 'vue'
interface Data {
title: string;
content: string;
id: number | string
}
const itemSize = ref(100)
const listData = ref<Data[]>([])
const screenHeight: number = document.documentElement.clientHeight || document.body.clientHeight
const visibleCount = ref(Math.ceil(screenHeight / itemSize.value))
const startOffset = ref(0)
const start = ref(0)
const end = ref(start.value + visibleCount.value)
const list = ref()
let indexId = 0
const listHeight = computed(() => listData.value.length * itemSize.value)
const getTransform = computed(() => `translate3d(0, ${startOffset}px, 0)`)
const visibleData = computed(() => listData.value.slice(start.value, Math.min(end.value, listData.value.length)))
const getTenListData = () => {
if(listData.value.length > 200){
return []
}
indexId++
return new Array(10).fill({}).map((item, index) => ({ id: index + indexId, title: `${indexId}name${index}`, content: `${indexId}content${index}` }))
}
const scrollEvent = () => {
const scrollTop = list.value.scrollTop
start.value = Math.floor(scrollTop / itemSize.value)
end.value = start.value + visibleCount.value
if(end.value > listData.value.length){
listData.value = listData.value.concat(getTenListData())
}
startOffset.value = scrollTop - (scrollTop % itemSize.value)
}
listData.value = getTenListData()
</script>
<style>
.scrollTopBtn {
position: fixed;
border-radius: 50%;
font-size: 12px;
color: white;
background: goldenrod;
bottom: 101px;
right: 20px;
z-index: 10000;
width: 50px;
height: 50px;
text-align: center;
line-height: 50px;
}
.infinite-list-container {
margin-top: 10px;
height: 99%;
overflow: scroll;
position: relative;
-webkit-overflow-scrolling: touch;
}
.infinite-list-phantom {
position: absolute;
left: 0;
top: 0;
right: 0;
z-index: -1;
}
.infinite-list {
left: 0;
right: 0;
top: 0;
position: absolute;
text-align: center;
}
.infinite-list-item {
background: white;
box-shadow: 0 0 10px rgba(144, 144, 144, 0.15);
border-radius: 5px;
display: flex;
align-items: center;
justify-content: center;
margin-top: 10px;
}
.left-section {
width: 25%;
display: flex;
justify-content: center;
align-items: center;
font-size: 25px;
font-weight: bold;
color: white;
background: #6ab6fc;
border-radius: 10px;
}
.right-section {
height: 100%;
margin-left: 20px;
flex: 1;
}
.title {
font-size: 14px;
font-weight: 500;
text-align: left;
height: 14px;
}
.desc {
margin-top: 10px;
font-size: 12px;
font-weight: 400;
text-align: left;
height: 12px;
}
</style>