import React, { memo, useMemo, useRef, useState } from 'react';
import { PageContainer } from '@ant-design/pro-components';
import './MemberPage.less';
type Props = {
// 滚动区域高度
height: number;
// 元素高度
itemHeight: number;
}
const data = Array.from({ length: 1000000 }, (_, i) => 1 + i);
const VirtualScroll: React.FC<Props> = ({ itemHeight = 50, height = 500}) => {
//可视区域高度
const screenHeight = useRef(height);
/** 列表总高度 */
const listHeight = useRef(data.length * itemHeight);
/** 显示元素数量 */
const visibleCount = useRef(Math.ceil(screenHeight.current / itemHeight));
//偏移量
const [startOffset, setStartOffset] = useState(0);
// 开始index
const [startIndex, setStartIndex] = useState(0);
// 结束index tips: 开始位置 + 显示元素数量
const [endIndex, setEndIndex] = useState(startIndex + visibleCount.current);
/** 当前需要渲染的元素内容 */
const visibleData = useMemo(() => {
return data.slice(startIndex, Math.min(endIndex, data.length));
}, [startIndex, endIndex]);
const getTransform = useMemo(() => {
return `translate3d(0, ${startOffset}px,0)`;
}, [startOffset]);
const handleScroll = (e) => {
// 当前滚动距离顶部高度
const scrollTop = e.target.scrollTop;
// 滚动高度 / 元素高度 = 当前滚动位置第一位元素下标
const index = Math.floor(scrollTop / itemHeight);
setStartIndex(index);
setEndIndex(index + visibleCount.current);
// 偏移量(滚动位置可能不能整除元素高度,需要设置一定位置的偏移量)
setStartOffset(scrollTop - (scrollTop % itemHeight));
}
return (
<PageContainer className='member_container'>
<div className='container' onScroll={handleScroll}>
{/* 滚动条 */}
<div className='list-progress' style={{ height: listHeight.current + 'px' }} />
{/* 滚动内容 */}
<div className='list' style={{ transform: getTransform }}>
{visibleData.map(item => <div key={item} className='list-item'>{item}</div>)}
</div>
</div>
</PageContainer>
);
};
export default memo(VirtualScroll);
css样式
.container {
height: 500px;
overflow: auto;
position: relative;
-webkit-overflow-scrolling: touch;
}
.list-progress {
position: absolute;
left: 0;
top: 0;
right: 0;
z-index: -1;
}
.list {
left: 0;
right: 0;
top: 0;
position: absolute;
text-align: center;
}
.list-item {
height: 50px;
}