!!!###!!!title=2.3 VTable 场景树渲染——VisActor/VTable 社区贡献者文档!!!###!!!!!!###!!!description=---title: 2.3 VTable 场景树渲染 key words: VisActor,VChart,VTable,VStrory,VMind,VGrammar,VRender,Visualization,Chart,Data,Table,Graph,Gis,LLM--- !!!###!!!

相关源码位置

  • package/vtable/src/scenegraph/group-creater/cell-helper.ts:VTable 渲染引擎中单元格处理的核心文件,提供了 用于根据不同列类型创建单元格(createCell )、负责单元格的动态更新(updateCell updateCellContent )等功能;
  • package/vtable/src/scenegraph/group-creater/column-helper.ts:基于createCell单元格创建实现了复合列表ComplexColumn的合并、渲染处理;
  • package/vtable/src/scenegraph/group-creater/column.ts:基于CreateComplexColumn从起始列到结束列,遍历创建列组,从而构建整个表格的列结构;

表格的创建流程

表格的场景图的构建过程是层层递进的,整体流程大致是先通过createCell函数创建单个单元格,然后利用createComplexColumn函数创建复合列(同一列中不同类型的单元格),最后通过createColGroup函数遍历生成表格的列组,进而形成整个表格的结构。

下面逐层分析创建的流程:

单元格的创建(

功能:根据传入的单元格类型type,创建相应类型的单元格。支持的类型包括textimagevideochartprogressbarsparklinecheckboxradio等。

function createCell(
  type: ColumnTypeOption, // 单元格类型(text/link/image/chart等)
  value: string,         // 单元格显示值
  define: ColumnDefine,  // 列定义配置
  table: BaseTableAPI,   // 表格实例
  col: number,           // 列索引
  row: number,           // 行索引
  colWidth: number,      // 列宽
  cellWidth: number,     // 单元格实际宽度(可能合并)
  cellHeight: number,    // 单元格高度
  columnGroup: Group,    // 父容器(列组)
  y: number,             // Y轴位置
  padding: [number, number, number, number], // 内边距
  textAlign: CanvasTextAlign,     // 文本对齐
  textBaseline: CanvasTextBaseline, // 文本基线
  mayHaveIcon: boolean,  // 是否可能包含图标
  cellTheme: IThemeSpec, // 主题样式
  range: CellRange | undefined, // 合并单元格范围
  customResult?: {     
    elementsGroup?: VGroup;
    renderDefault: boolean;
  }
): Group    

这个函数源码的大部分结构是根据不同的CellType进入不同的创建流程,对创建的Group对象通过Factory.getFunction动态获取各类型创建函数,然后配置不同单元格特有的参数或逻辑处理,

列的创建(createComplexColumn)

功能:在指定的列范围内,根据单元格的合并情况、数据类型等,创建复合列,计算或获取合并单元格的总宽度和高度,并通过Map缓存优化性能。

确认插入坐标坐标

复杂列的创建一开始先确认插入的坐标,根据 columnGroup 的最后一个子元素和高度,确定单元格插入的起始Y坐标;如果连续插入单元格的话,就跟在现有的高度上累加上单元格的高度,从而形成列的结构。这里处理了连续添加单元格时的垂直定位问题。

let y = 0;
if (columnGroup.lastChild && (columnGroup.lastChild as Group).row === rowStart - 1) {
  y = (columnGroup.lastChild as Group).attribute.y + (columnGroup.lastChild as Group).attribute.height;
} else if (columnGroup.colHeight) {
  y = columnGroup.colHeight;
}
    

合并单元格dealMerge

接着从起始行至终止行开始执行遍历循环,处理单元格的合并算法;处理单元格的合并,即通过计算更新单元格的宽度和高度(计算mergeSize)实现合并的效果。此处的缓存指的是以起始行列的拼接值做键,通过Map缓存mergeResult ,存储计算的CellWitdh和CellHeight值,避免重复计算

function dealMerge(range: CellRange, mergeMap: MergeMap, table: BaseTableAPI, forceUpdate: boolean) {
  let cellWidth = 0;
  let cellHeight = 0;
  const mergeResult = mergeMap.get(`${range.start.col},${range.start.row};${range.end.col},${range.end.row}`);
  if (!mergeResult || forceUpdate) {
    for (let col = range.start.col; col <= range.end.col; col++) {
      cellWidth += table.getColWidth(col);
    }

    // let cellHeight = 0;
    for (let i = range.start.row; i <= range.end.row; i++) {
      cellHeight += table.getRowHeight(i);
    }

    mergeMap.set(`${range.start.col},${range.start.row};${range.end.col},${range.end.row}`, {
      cellWidth,
      cellHeight
    });
  } else {
    cellWidth = mergeResult.cellWidth;
    cellHeight = mergeResult.cellHeight;
  }
  return {
    cellWidth,
    cellHeight
  };
}    

表格的创建(createColGroup)

功能:在指定的列和行范围内,创建列组,从而构建整个表格的列结构。

for (let i = colStart; i <= colEnd; i++) {
    const col = i;
    const colWidth = table.getColWidth(col);

    const columnGroup = new Group({
      x: xOrigin + x,
      y: yOrigin,
      width: colWidth,
      height: 0,
      clip: false,
      pickable: false
    });
    columnGroup.role = 'column';
    columnGroup.col = i;
    containerGroup.addChild(columnGroup);
    const { width: default2Width, height: default2Height } = createComplexColumn(
      columnGroup,
      col,
      colWidth,
      rowStart,
      rowEnd,
      table.scenegraph.mergeMap,
      cellLocation === 'columnHeader' && isNumber(defaultHeaderRowHeight)
        ? (defaultHeaderRowHeight as number)
        : defaultRowHeight,
      table,
      // cellLocation,
      rowLimit
    );
    x += default2Width;
    heightMax = Math.max(heightMax, default2Height);
  }    

这段代码首先从起始列到末尾列依次遍历,依次执行:

  • 创建列组对象columnGroupx: xOrigin + x理解起来比较形象,x (偏移量)随着循环创建列组而不断累加,不难想象由此每一列组都能依次排列在水平方向上;width: colWidth设置列组的宽度为当前列的宽度 colWidth,该宽度通过 table.getColWidth(col) 获取;height: 0初始时将列组的高度设置为 0,后续会根据该列中实际创建的元素(如单元格)的高度进行更新。

  • 设置列组属性和索引,设置 role 属性为 'column',列组设置 col 属性为当前列的索引 i,这样可以明确该列组对应的是表格中的哪一列。

  • 通过调用 createComplexColumn 函数,在指定的列组中创建ComplexColumn复合列,并动态更新布局参数。createComplexColumn 函数接收多个参数,包括列组、列索引、列宽、行范围等,同时会考虑单元格的位置、合并情况以及默认行高,最终生成相应的列结构。

本文档由以下人员修正整理

玄魂