Loading...
本文会介绍透视表的布局过程,让读者更直接了解 S2
内部布局逻辑。
主要流程:
在数据处理完成后,就会进入布局阶段,主要分为布局生成,和实时渲染。布局生成后会重复使用,直到配置信息发生改变为止;表格会根据用户的交互信息做实时渲染。
以下图透视表为例:
以下的代码都是伪代码,只是为了说明关键步骤,布局处理逻辑大部分都在 facet 文件夹中。
生成各种各样的 layerGroup 信息,包括 backgroundGroup
,foregroundGroup
, panelGroup
,panelScrollGroup
和各类冻结 frozenGroup
等等。
生成行头,列头层级节点信息,并且计算好每个单元格的尺寸信息。可以通过 S2 实例中 facet.getLayoutResult()
获取各种节点信息:
生成 PanelBBox 和 CornerBBox 结构,这两个BBox 主要用于后续用作角头,行列头,数据单元格区域的尺寸标识,和 clip 裁切
绑定鼠标滚轮事件,分页事件等。
将表格的滚动数据调整到合理的范围了,比如表格宽度突然发生变小,原本的滚动条已经滚动到100%的状态的前提下,需要根据当前的宽度重新调整,否则会出现偏移过多,出现空白的情况。
渲染行头、列头、角头,滚动条,背景色等等。
对于表格的单元格,在大量数据的情况下,全量单元格会非常多,所以 S2
的策略是渲染可视区域内的单元格(按需渲染),滚动后动态新增和删除单元格,达到性能最佳。
通过 scrollX
和 scrollY
,计算当前可视视窗中的单元格的节点索引。
const indexes = this.calculateXYIndexes(scrollX, scrollY); // indexes 结果是当前可视视窗节点的坐标索引集合。
对比当前可视视窗节点索引集合和上次索引集合的差值。
const { add, remove } = diffPanelIndexes(this.preCellIndexes, indexes); // add 和 remove 也是节点坐标索引集合
针对新增和删除的节点分别进行渲染和删除。
each(add, (x, y) => renderCell(x, y));each(remove, (x, y) => getCell(x, y).remove());
通过按需渲染,极大的提高了 S2
的渲染效率,这也是我们支持百万级别数据渲染的原因。
对于数据的频繁读取,我们也做了性能提升,比如缓存数据读取结果、缓存字体宽度计算等。
/*** 查找字段信息*/public getFieldMeta = memoize((field: string, meta?: Meta[]): Meta => {return find(this.meta || meta, (m: Meta) => m.field === field);});
另外,S2
还做了其他的一些优化来提升渲染效率,比如滚动合帧(解决滚动白屏问题)、懒渲染(解决多次重复渲染问题)。
行列冻结相关的处理都被抽象到 FrozenFacet
中处理,会重新基础的实时渲染链路,增加对各类冻结 frozenGroup
的绘制步骤。
被冻结的行列,在布局上会有特殊处理。简单的说,冻结行列格子的定位是固定的。其中: