transform: translate3d(x,y,z) 代替 position: absolute定位实现虚拟滚动
image.png

  • 不能把 transform加到 height=40000的 div上面,会出现滚动条【无法滚动到低的Bug】

FixedSizeList

  1. import React, { useState, useEffect, useRef } from 'react';
  2. import PropTypes from 'prop-types';
  3. import classNames from 'classnames';
  4. FixedSizeList.propTypes = {
  5. width: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
  6. height: PropTypes.number,
  7. itemSize: PropTypes.number,
  8. itemCount: PropTypes.number,
  9. className: PropTypes.string,
  10. };
  11. function FixedSizeList(props) {
  12. const { width, height, itemSize, itemCount, className, children } = props;
  13. const rootRef = useRef(null);
  14. const [scrollTop, setScrollTop] = useState(0);
  15. // 要显示的元素起始和结束索引
  16. const [startIndex, setStartIndex] = useState(0);
  17. const CHILDREN = [];
  18. // 可视区显示多少个
  19. const pageSize = Math.ceil(height / itemSize);
  20. const endIndex = startIndex + pageSize;
  21. useEffect(init, []);
  22. function init() {
  23. const el = rootRef.current;
  24. el.addEventListener('scroll', onScroll);
  25. return () => {
  26. el.removeEventListener('scroll', onScroll);
  27. };
  28. }
  29. // 滚动时,重新计算开始的索引
  30. function onScroll(e) {
  31. let { scrollTop } = e.target;
  32. const _endIndex = itemCount - endIndex;
  33. // 起始值要向下取整
  34. let index = Math.floor(scrollTop / itemSize);
  35. if (index >= _endIndex) index = _endIndex;
  36. setStartIndex(index);
  37. setScrollTop(scrollTop);
  38. console.log('e', scrollTop, scrollTop % itemSize, index);
  39. }
  40. const style = { width: '100%', height: itemSize };
  41. for (let i = startIndex; i < endIndex; i++) {
  42. CHILDREN.push(children({ index: i, style }));
  43. }
  44. const rootClass = classNames({[classNames]: Boolean(className)});
  45. const rootStyle = { width, height, position: 'relative', overflow: 'auto' };
  46. // 核心style,设置滚动时的高度
  47. const transformStyle = {
  48. transform: `translate3d(0, ${scrollTop}px, 0)`,
  49. transition: 'transform 60ms linear',
  50. willChange: 'transform',
  51. };
  52. return (
  53. <div
  54. style={rootStyle}
  55. ref={rootRef}
  56. className={rootClass}
  57. >
  58. <div style={{ height: itemSize * itemCount }}>
  59. <div style={transformStyle}>
  60. {CHILDREN}
  61. </div>
  62. </div>
  63. </div>
  64. );
  65. }
  66. export default FixedSizeList

index.less

  1. :global {
  2. .odd {
  3. background-color: rgba(156, 179, 197, 0.26);
  4. }
  5. .scrollbar{
  6. &::-webkit-scrollbar{
  7. width:8px;
  8. }
  9. &::-webkit-scrollbar-track{
  10. background-color: #fafafa;
  11. border-radius: 4px;
  12. }
  13. // 滚动部分的样式
  14. &::-webkit-scrollbar-thumb{
  15. background-color: rgba(0, 0, 0, 0.45);
  16. border-radius: 8px;
  17. transition: background-color 300ms linear;
  18. &:hover {
  19. background-color: rgba(0, 0, 0, 0.65)
  20. }
  21. }
  22. // 2个滚动条对齐的样式
  23. &::-webkit-scrollbar-corner{
  24. background-color: rgba(187,187,187, 1);
  25. }
  26. }
  27. }