antd5 虚拟列表原理(rc-virtual-list)

github:https://github.com/react-component/virtual-list

rc-virtual-list 版本 3.11.4(2024-02-01)
版本:virtual-list-3.11.4

在这里插入图片描述

Development

npm install
npm start
open http://localhost:8000/

在这里插入图片描述

List 组件接收 Props

PropDescriptionTypeDefault
childrenRender props of item(item, index, props) => ReactElement-
componentCustomize List dom elementstring | Componentdiv
dataData listArray-
disabledDisable scroll check. Usually used on animation controlbooleanfalse
heightList heightnumber-
itemHeightItem minium heightnumber-
itemKeyMatch key with itemstring-
stylesstyle{ horizontalScrollBar?: React.CSSProperties; horizontalScrollBarThumb?: React.CSSProperties; verticalScrollBar?: React.CSSProperties; verticalScrollBarThumb?: React.CSSProperties; }-

组件解析

import ResizeObserver from "rc-resize-observer";

const onHolderResize: ResizeObserverProps["onResize"] = (sizeInfo) => {
  console.log("sizeInfo", sizeInfo);

  setSize({
    width: sizeInfo.width || sizeInfo.offsetWidth,
    height: sizeInfo.height || sizeInfo.offsetHeight,
  });
};

// 用于监听dom节点resize时返回dom节点信息
<ResizeObserver onResize={onHolderResize}></ResizeObserver>;

打印的 sizeInfo

{
  height: 200,//可视区高度
  offsetHeight: 200,
  offsetWidth: 606,
  width: 606,//可视区宽度
}
 //component: Component = 'div',
// Component默认是div标签 ,className为rc-virtual-list-holder, 是虚拟列表的可视化区域
<Component
    className={`${prefixCls}-holder`}
    style={componentStyle}
    ref={componentRef}
    onScroll={onFallbackScroll}
    onMouseEnter={delayHideScrollBar}
  >

componentStyle 计算,是一个 styles 对象 React.CSSProperties

const ScrollStyle: React.CSSProperties = {
  overflowY: "auto",
  overflowAnchor: "none",
};

// useVirtual: 是否虚拟列表(属性virtual为true 并且height和itemHeight有值)
const useVirtual = !!(virtual !== false && height && itemHeight);

let componentStyle: React.CSSProperties = null;
if (height) {
  componentStyle = {
    [fullHeight ? "height" : "maxHeight"]: height,
    ...ScrollStyle,
  };

  if (useVirtual) {
    componentStyle.overflowY = "hidden";

    if (scrollWidth) {
      componentStyle.overflowX = "hidden";
    }

    if (scrollMoving) {
      componentStyle.pointerEvents = "none";
    }
  }
}

overflow-anchor CSS 属性提供一种退出浏览器滚动锚定行为的方法,该行为会调整滚动位置以最大程度地减少内容偏移。
默认情况下,在任何支持滚动锚定行为的浏览器中都将其启用。因此,仅当你在文档或文档的一部分中遇到滚动锚定问题并且需要关闭行为时,才通常需要更改此属性的值。

内容组件
import Filler from ‘./Filler’;

<Filler
  prefixCls={prefixCls}
  height={scrollHeight}
  offsetX={offsetLeft}
  offsetY={fillerOffset}
  scrollWidth={scrollWidth}
  onInnerResize={collectHeight}
  ref={fillerInnerRef}
  innerProps={innerProps}
  rtl={isRTL}
  extra={extraContent}
>
  {listChildren}
</Filler>

Filler 组件

<div style={outerStyle}>
  <ResizeObserver
    onResize={({ offsetHeight }) => {
      if (offsetHeight && onInnerResize) {
        onInnerResize();
      }
    }}
  >
    <div
      style={innerStyle}
      className={classNames({
        [`${prefixCls}-holder-inner`]: prefixCls,
      })}
      ref={ref}
      {...innerProps}
    >
      {children}
      {extra}
    </div>
  </ResizeObserver>
</div>

demo 查看渲染内容

在这里插入图片描述

outStyle 计算:

let outerStyle: React.CSSProperties = {};

if (offsetY !== undefined) {
  // Not set `width` since this will break `sticky: right`
  outerStyle = {
    height,
    position: "relative",
    overflow: "hidden",
  };
}

innerStyle 计算

let innerStyle: React.CSSProperties = {
  display: "flex",
  flexDirection: "column",
};
if (offsetY !== undefined) {
  innerStyle = {
    ...innerStyle,
    transform: `translateY(${offsetY}px)`,
    [rtl ? "marginRight" : "marginLeft"]: -offsetX,
    position: "absolute",
    left: 0,
    right: 0,
    top: 0,
  };
}

可以看到最终渲染的元素,有下面几个容器组成:

列表容器:rc-virtual-list
列表内容容器:rc-virtual-list-holder

要点:
Component 组件,默认 div:固定高度,超出部分隐藏,最终也是通过控制该容器的滚动高度来达到元素滚动的目的

div(outStyle):高度为所有列表内容都渲染出来的高度,这里是为了撑开父元素,实现父元素的滚动
渲染列表容器:rc-virtual-list-holder-inner
单个列表内容:item

listChildren

const listChildren = useChildren(
  mergedData, //列表数据
  start, //渲染第一个元素的索引
  end, //渲染最后一个元素的索引
  scrollWidth,
  setInstanceRef, //获取元素
  children,
  sharedConfig
);

useChildren 主要是进行 list 列表的渲染,而在渲染列表时,又用 Item 组件进行了一层包裹.

export default function useChildren<T>(
  list: T[],
  startIndex: number,
  endIndex: number,
  scrollWidth: number,
  setNodeRef: (item: T, element: HTMLElement) => void,
  renderFunc: RenderFunc<T>,
  { getKey }: SharedConfig<T>
) {
  return list.slice(startIndex, endIndex + 1).map((item, index) => {
    const eleIndex = startIndex + index;
    const node = renderFunc(item, eleIndex, {
      style: {
        width: scrollWidth,
      },
    }) as React.ReactElement;

    const key = getKey(item);
    return (
      <Item key={key} setRef={(ele) => setNodeRef(item, ele)}>
        {node}
      </Item>
    );
  });
}

Item 组件
用 Item 组件包裹了外部传入的列表元素的 JSXElement

export interface ItemProps {
  children: React.ReactElement;
  setRef: (element: HTMLElement) => void;
}

export function Item({ children, setRef }: ItemProps) {
  const refFunc = React.useCallback((node) => {
    setRef(node);
  }, []);

  return React.cloneElement(children, {
    ref: refFunc,
  });
}

经过这么一层包装,当通过 ref 获取子节点时,将会调用 refFunc -> setRef -> setInstanceRef。这也是为什么当元素高度可变时需要用 React.forwardRef 进行列表元素的包裹

滚动条组件

<ScrollBar
  ref={verticalScrollBarRef}
  prefixCls={prefixCls}
  scrollOffset={offsetTop}
  scrollRange={scrollHeight}
  rtl={isRTL}
  onScroll={onScrollBar} //滚动事件
  onStartMove={onScrollbarStartMove} //开始滚动事件
  onStopMove={onScrollbarStopMove} //滚动结束事件
  spinSize={verticalScrollBarSpinSize}
  containerSize={size.height}
  style={styles?.verticalScrollBar}
  thumbStyle={styles?.verticalScrollBarThumb}
/>

ScrollBar 渲染

<div
  ref={scrollbarRef}
  className={classNames(scrollbarPrefixCls, {
    [`${scrollbarPrefixCls}-horizontal`]: horizontal,
    [`${scrollbarPrefixCls}-vertical`]: !horizontal,
    [`${scrollbarPrefixCls}-visible`]: visible,
  })}
  style={{ ...containerStyle, ...style }}
  onMouseDown={onContainerMouseDown}
  onMouseMove={delayHidden}
>
  <div
    ref={thumbRef}
    className={classNames(`${scrollbarPrefixCls}-thumb`, {
      [`${scrollbarPrefixCls}-thumb-moving`]: dragging,
    })}
    style={{ ...thumbStyle, ...propsThumbStyle }}
    onMouseDown={onThumbMouseDown}
  />
</div>

通过滚动条组件滚动事件

//newScrollOffset 滚动的距离,horizontal是否水平滚动方向
function onScrollBar(newScrollOffset: number, horizontal?: boolean) {
  const newOffset = newScrollOffset;
  if (horizontal) {
    flushSync(() => {
      setOffsetLeft(newOffset);
    });
    triggerScroll();
  } else {
    syncScrollTop(newOffset);
  }
}

滚动条开始滚动事件和滚动结束事件

// 滚动开始事件
const onScrollbarStartMove = () => {
  console.log("----start-----");

  setScrollMoving(true);
};

//滚动结束事件
const onScrollbarStopMove = () => {
  console.log("-----end");

  setScrollMoving(false);
};

注意点

  • 如果子项存在动态高度或者高度不统一的情况,需要使用 React.forwardRef 转发 ref 给子 DOM 元素。
  • 列表项之间不要存在上下间距( margin-top 、 margin-bottom )。
    以上两点如果没有做到,调用组件的 scrollTo(scrollConfig) 方法进行滚动时都会导致滚动位置异常

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/462800.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【计算机网络】https的工作原理以及和http的区别

目录 前言 1. HTTP协议存在的问题 2. 什么是HTTPS协议&#xff1f; 3. HTTP和HTTPS有哪些区别&#xff1f; 4. HTTPS的工作原理 加密方式 前言 在日常的Web项目练习中&#xff0c;我们会发现老师会让我们在打开服务器之后使用 http://localhost/...进行项目效果测试和预览…

OpenCV C++ 图像处理实战 ——《物体数量计数与尺寸测量》

OpenCV C++ 图像处理实战 ——《物体数量计数与尺寸测量》 一、结果演示二、多尺度自适应Gamma矫正的低照度图像增强三、轮廓计算与尺寸测量3.1 图像二值化3.2 轮廓提取3.3 物体计数与尺寸测量四、源码测试图像下载总结一、结果演示 二、多尺度自

1.实用Qt:解决绘制圆角边框时,圆角锯齿问题

目录 问题描述 解决方案 方案1&#xff1a; 方案2&#xff1a; 结果示意图 问题描述 做UI的时候&#xff0c;我们很多时候需要给绘制一个圆角边框&#xff0c;初识Qt绘制的童鞋&#xff0c;可能绘制出来的圆角边框很是锯齿&#xff0c;而且粗细不均匀&#xff0c;如下图&…

开环端到端自动驾驶: 到底行不行

开环端到端自动驾驶&#xff1a; 到底行不行 附赠全面专业的自动驾驶学习资料&#xff1a;直达链接 TLDR: 别在nuScenes上做开环端到端自动驾驶刷点了。 论文&#xff1a; https://arxiv.org/pdf/2312.03031.pdf github: https://github.com/NVlabs/BEV-Planner 前言 Uni…

idea中database的一些用法

1、查看表结构 方法1&#xff0c;右键&#xff0c;选这个 方法2 双击表后&#xff0c;看到数据&#xff0c;点DDL 方法3 写SQL时&#xff0c;把鼠标放在表名上&#xff0c;可以快速查看表结构 2、表生成对应的实体类 表中右键&#xff0c;选择这2个&#xff0c;选择生成的路…

物联网 3.15日 | 2024年中国七大 IoT 物联网云平台价格对比

随着 中国电信天翼 CTWing 物联网平台正式开始收费&#xff0c;国内物联网平台云产品发展进入成熟期&#xff0c;越来越多企业选择云厂商提供的物联网PaaS服务&#xff0c;以降低运营成本&#xff0c;缩短业务上线周期&#xff0c;释放运维的人力&#xff0c;按需付费动态扩容。…

2024 第一届VCTF 纳新赛 Web方向 题解WP

hackjs 题目描述&#xff1a;A baby oldjs, just warm up. 附件给源码 const express require(express) const fs require(fs) var bodyParser require(body-parser); const app express() app.use(bodyParser.urlencoded({extended: true })); app.use(bodyParser.json…

安装python、pycharm,打好基础,准备飞起

python安装使用 安装python安装包 以下为自定义安装python安装包&#xff0c;无特殊要求可直接进行安装。 勾选Add Python 3.6 to PATH&#xff0c; 然后点击 Customize installation&#xff0c;进行自定义安装。 所有的都勾上&#xff0c;然后点击Next。 可选择自己需要…

【算法杂货铺】模拟

目录 &#x1f308;前言&#x1f308; &#x1f4c1;1576. 替换所有的问号​编辑 &#x1f4c1; 495. 提莫攻击 &#x1f4c1; 6. Z 字形变换 &#x1f4c1;38. 外观数列 &#x1f4c1;1419. 数青蛙 &#x1f4c1; 总结 &#x1f308;前言&#x1f308; 欢迎观看本期【算…

鸿蒙Harmony应用开发—ArkTS声明式开发(容器组件:Stack)

堆叠容器&#xff0c;子组件按照顺序依次入栈&#xff0c;后一个子组件覆盖前一个子组件。 说明&#xff1a; 该组件从API Version 7开始支持。后续版本如有新增内容&#xff0c;则采用上角标单独标记该内容的起始版本。 子组件 可以包含子组件。 接口 Stack(value?: { ali…

「SpringBrick快速入门指南」:一款基于Spring Boot的高级插件化开发框架

文章目录 关于 | About技术文档 | Document开源项目 | Project 案例 | Demo项目结构 | Structure主程序配置集成 | Settings引入框架依赖 | Framework在配置文件加入配置 | YamlSpringBoot启动类改引导类 | Change 插件配置集成 | Settings引入依赖 | XML定义插件引导类 | Clas…

计算机服务器中了360后缀勒索病毒怎么办,勒索病毒解密工具与流程

对于众多的企业来说&#xff0c;利用网络开展各项工作业务是必不可少的环节&#xff0c;网络为企业的生产运营提供了有利条件&#xff0c;但网络是一把双刃剑&#xff0c;在为人们提供便利的同时&#xff0c;也为企业的数据安全带来严重威胁。近期&#xff0c;云天数据恢复中心…

Linux 基础-查看和设置环境变量

一&#xff0c;查看环境变量 在 Linux中&#xff0c;环境变量是一个很重要的概念。环境变量可以由系统、用户、Shell 以及其他程序来设定&#xff0c;其是保存在变量 PATH 中。环境变量是一个可以被赋值的字符串&#xff0c;赋值范围包括数字、文本、文件名、设备以及其他类型…

Linux系统——Session ID(负载均衡如何保持会话)

目录 一、实验环境搭建 二、部署Nginx代理服务器配置 三、部署后端真是服务器Tomcat配置 四、配置Tomcat的Session ID会话保持 五、测试 此次实验是Tomcat后端服务器如何做Session ID会话保持 一、实验环境搭建 [rootlocalhost ~]#systemctl stop firewalld [rootlocalho…

实战:django项目环境搭建(pycharm,virtualBox)

django项目环境搭建 一.创建虚拟环境二.创建PyCharm远程连接 一.创建虚拟环境 需要用到的软件&#xff1a;PyCharm&#xff0c;VirtualBox虚拟机。 1.打开虚拟机终端&#xff0c;创建新的虚拟环境 Book。 2.在虚拟环境中创建新的文件夹 library&#xff0c;cd命令进入该文件…

MIT线性代数-方程组的几何解释

文章目录 1. 二维空间1.1 行方向1.2 列方向 2. 三维空间2.1 行方向2.2 列方向 假设有一个方程组 A X B AXB AXB表示如下 2 x − y 0 (1) 2x-y0\tag{1} 2x−y0(1) − x 2 y 3 (2) -x2y3\tag{2} −x2y3(2) 矩阵表示如下&#xff1a; [ 2 − 1 − 1 2 ] [ x y ] [ 0 3 ] (3)…

数据分析-Pandas的直接用Matplotlib绘图

数据分析-Pandas的直接用Matplotlib绘图 数据分析和处理中&#xff0c;难免会遇到各种数据&#xff0c;那么数据呈现怎样的规律呢&#xff1f;不管金融数据&#xff0c;风控数据&#xff0c;营销数据等等&#xff0c;莫不如此。如何通过图示展示数据的规律&#xff1f; 数据表…

Linux第80步_使用“信号量”实现“互斥访问”共享资源

1、创建MySemaphoreLED目录 输入“cd /home/zgq/linux/Linux_Drivers/回车” 切换到“/home/zgq/linux/Linux_Drivers/”目录 输入“mkdir MySemaphoreLED回车”&#xff0c;创建“MySemaphoreLED”目录 输入“ls回车”查看“/home/zgq/linux/Linux_Drivers/”目录下的文件…

基于opencv的图像处理系统的设计与实现

概要 随着计算机技术的飞速发展&#xff0c;图像技术在各领域的研究和应用日渐深入和广泛。opencv是近年来推出的开源、免费的计算机视觉库,利用其所包含的函数可以很方便地实现数字图像处理。本文旨在对opencv进行一个快速全面简介,通过介绍图像处理的相关函数&#xff0c;使读…

Git学习记录

目录 Git Git介绍 版本控制 版本控制工具 集中式版本控制工具 分布式版本控制工具 Git工作机制 ​编辑 Git和代码托管中心 Git安装 Git常用命令 设置用户签名 初始化本地库 查看本地库状态 添加到暂存区 提交到本地库 修改文件 历史版本 查看历史版本 版本…