transform: translate3d(x,y,z) 代替 position: absolute定位实现虚拟滚动
- 不能把 transform加到 height=40000的 div上面,会出现滚动条【无法滚动到低的Bug】
FixedSizeList
import React, { useState, useEffect, useRef } from 'react';
import PropTypes from 'prop-types';
import classNames from 'classnames';
FixedSizeList.propTypes = {
width: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
height: PropTypes.number,
itemSize: PropTypes.number,
itemCount: PropTypes.number,
className: PropTypes.string,
};
function FixedSizeList(props) {
const { width, height, itemSize, itemCount, className, children } = props;
const rootRef = useRef(null);
const [scrollTop, setScrollTop] = useState(0);
// 要显示的元素起始和结束索引
const [startIndex, setStartIndex] = useState(0);
const CHILDREN = [];
// 可视区显示多少个
const pageSize = Math.ceil(height / itemSize);
const endIndex = startIndex + pageSize;
useEffect(init, []);
function init() {
const el = rootRef.current;
el.addEventListener('scroll', onScroll);
return () => {
el.removeEventListener('scroll', onScroll);
};
}
// 滚动时,重新计算开始的索引
function onScroll(e) {
let { scrollTop } = e.target;
const _endIndex = itemCount - endIndex;
// 起始值要向下取整
let index = Math.floor(scrollTop / itemSize);
if (index >= _endIndex) index = _endIndex;
setStartIndex(index);
setScrollTop(scrollTop);
console.log('e', scrollTop, scrollTop % itemSize, index);
}
const style = { width: '100%', height: itemSize };
for (let i = startIndex; i < endIndex; i++) {
CHILDREN.push(children({ index: i, style }));
}
const rootClass = classNames({[classNames]: Boolean(className)});
const rootStyle = { width, height, position: 'relative', overflow: 'auto' };
// 核心style,设置滚动时的高度
const transformStyle = {
transform: `translate3d(0, ${scrollTop}px, 0)`,
transition: 'transform 60ms linear',
willChange: 'transform',
};
return (
<div
style={rootStyle}
ref={rootRef}
className={rootClass}
>
<div style={{ height: itemSize * itemCount }}>
<div style={transformStyle}>
{CHILDREN}
</div>
</div>
</div>
);
}
export default FixedSizeList
index.less
:global {
.odd {
background-color: rgba(156, 179, 197, 0.26);
}
.scrollbar{
&::-webkit-scrollbar{
width:8px;
}
&::-webkit-scrollbar-track{
background-color: #fafafa;
border-radius: 4px;
}
// 滚动部分的样式
&::-webkit-scrollbar-thumb{
background-color: rgba(0, 0, 0, 0.45);
border-radius: 8px;
transition: background-color 300ms linear;
&:hover {
background-color: rgba(0, 0, 0, 0.65)
}
}
// 2个滚动条对齐的样式
&::-webkit-scrollbar-corner{
background-color: rgba(187,187,187, 1);
}
}
}