记录虚拟列表的简单实现

118 阅读1分钟

主要思路

记录列表滚动时在容器中的位置,并截取在容器中的内容,而容器视图外的内容利用可变的padding填充。

关键在于获取虚拟列表的开始和结束位置下标


const ChildScroll = (props: any) => {
  const { children, itemHeight = 50 } = props;

  console.log("滚动子组件-----", props);

  const containerRef = useRef();
  const [virtualParam, setVirtual] = useState({
    startIndex: 0,
    endIndex: 9,
  });

  const scrollHandle = (e) => {
    console.log("滚动事件------", e);
    console.log("滚动事件current------", containerRef.current);

    const length = children?.length || 0;
    // 容器高度
    const containerHeight = containerRef.current?.clientHeight || 600;
    // 内容总高度
    const contentHeight = length * itemHeight;
    // 可视区展示数量
    const viewNums = Math.ceil(containerHeight / itemHeight);

    const _startIndex = Math.floor(
      (containerRef?.current?.scrollTop || 0) / itemHeight
    );

    const startIndex = Math.max(_startIndex - Math.floor(viewNums / 2), 0);
    const endIndex = _startIndex + Math.floor(viewNums / 2) + viewNums;

    // const startIndex = _startIndex;
    // const endIndex = _startIndex + viewNums;

    console.log("开始下标-----", startIndex, "结束下标------", endIndex);
    setVirtual({
      startIndex,
      endIndex,
    });
  };

  const renderLIst = () => {
    return children.slice(virtualParam.startIndex, virtualParam.endIndex + 1);
  };

  const length = children?.length || 0;
  const paddingTop = virtualParam.startIndex * itemHeight;
  const paddingBotttom = (length - virtualParam.endIndex - 1) * itemHeight;

  return (
    <div
      style={{
        height: "500px",
        overflow: "scroll",
      }}
      onScroll={(e) => {
        scrollHandle(e);
      }}
      ref={containerRef}
    >
      <div
        className="childScroll"
        style={{
          padding: `${paddingTop}px 0 ${paddingBotttom}px 0`,
        }}
      >
        {/* {children} */}
        {renderLIst()}
      </div>
    </div>
  );
};