!!!###!!!title=图元基础概念——VisActor/VChart 社区贡献者文档!!!###!!!!!!###!!!description=---title: 6.1 图元的基本概念 key words: VisActor,VChart,VTable,VStrory,VMind,VGrammar,VRender,Visualization,Chart,Data,Table,Graph,Gis,LLM--- !!!###!!!

基本概念

在 VChart 中,**图元(Mark)**是构成图表的基本绘图单元,代表图表中可视化的基本元素,如点、线、矩形、多边形等。一般来说,图元的使用者主要包括:

  • Series:图表中用来展示数据的部分,不同种类的系列由不同种类的图元构成,例如,线图由点图元(端点)和线图元构成,饼图主要由弧线图元和区域图元构成......此时,系列所包含的图元的类型因系列的类型得以确定。

  • Component:图表中提供辅助能力,帮助图标阅读和交互的图表元素,如坐标轴、图例、标注等等,这些组件也是由图元构成的。由此看到,图元不仅仅是用来展示数据的,还是构成图表中各种元素的基本单元。

图元的作用

总体来说,图元的作用可以总结为:

  • 构成图表
  • 组成图表的各种组件,例如图例项由symbol图元和text图元组成。
  • 展示数据
  • 数据映射:图元将数据映射为可视化元素,使抽象的数据变得直观易懂。

  • 信息传达:通过不同类型和样式的图元,传达数据的特征和趋势。

  • 交互支持:图元支持用户与图表的交互,如悬停、点击等操作,增强数据的可探索性。

图元的类型总览

VChart 定义了多种基本和组合图元类型,包括但不限于:

基本图元:

  • **符号(symbol):**用于表示数据点的基本图形,如散点图中的点。

  • **矩形(rect):**用于绘制柱状图中的柱形等。

  • **线(line):**用于连接数据点,常见于折线图。

  • **直线(rule):**绘制特定位置的直线,可用于标示阈值等。

  • **弧线(arc):**用于绘制弧形,如饼图的扇区。

  • **区域(area):**表示填充的区域,常用于面积图。

  • **文本(text):**在图表中添加文字说明。

  • **路径(path):**绘制任意形状的路径。

  • **图像(image):**在图表中嵌入图片。

  • **3D 矩形(rect3d):**用于绘制三维柱状图等。

  • **3D 弧线(arc3d):**用于绘制三维饼图的扇区等。

  • **多边形(polygon):**绘制多边形区域。

**自定义图元:**详见

代码结构

实现图元的核心代码入口为packages/vchart/src/mark,本小节对代码结构以及功能进行简单描述,关于图元的具体逻辑见 。

mark模块可划分为以下几个核心模块:

其中:

  • 所有具体 Mark 继承自 BaseMark,并通过 CompilableMark 实现与 vgrammar 的桥接

  • 所有 Mark 实现 IMarkRaw 接口,关于样式系统需要实现 IVisualSpecStyle 接口

图元所处的继承关系链:

  • 基础模块 (base/)
  • 核心文件:base-mark.ts

  • 功能概览:所有图元的基类,包含图元共有的属性和操作。如关键的属性有:

  // 存储图元的样式状态,包括 normal、hover、selected 等状态的样式配置。
  declare stateStyle: IMarkStateStyle<T>;
  
  // 图元的初始化选项,包含上下文、全局缩放、模型等信息。
  protected declare _option: IMarkOption;

  // 图元的属性上下文,通常与图元的使用者(如 series 或 component)相关。
  protected _attributeContext: IModelMarkAttributeContext;

  // 扩展通道,用于在计算通道时添加默认通道以确保更新有效。
  _extensionChannel: {
    [key: string | number | symbol]: string[];
  } = {};
  
  // 扩展计算通道,用于在计算属性时添加额外的计算逻辑。
  _computeExChannel: {
    [key: string | number | symbol]: ExChannelCall;
  } = {};    

对外提供的关键接口有:

// *根据 spec 初始化 style*
initStyleWithSpec(spec: IMarkSpec<T>, key?: string);

// 设置图元style
setStyle<U extends keyof T>(style: Partial<IMarkStyle<T>>,state: StateValueType = 'normal',level: number = 0,stateStyle = this.stateStyle);

// 获取style
getStyle(key: string, state: StateValueType = 'normal'): any {return this.stateStyle[state][key]?.style;};

// 计算属性值 这些属性值最终会被传递给底层渲染引擎(如 VGrammar)进行绘制
getAttribute<U extends keyof T>(key: U, datum: Datum, state: StateValueType = 'normal', opt?: IAttributeOpt);

// 设置属性值
setAttribute<U extends keyof T>(attr: U,style: MarkInputStyle<T[U]>,state: StateValueType = 'normal',level: number = 0,stateStyle = this.stateStyle);    

  • 接口定义 (interface/)
  • 核心文件:common.ts

  • 关键接口:

// IMarkRaw 
export interface IMarkRaw<T extends ICommonSpec> extends ICompilableMark {
  // 图元状态样式(例如:hover,selected分别是什么样式)
  readonly stateStyle: IMarkStateStyle<T>;
  // 图元属性
  getAttribute: <U extends keyof T>(key: U, datum: any, state?: StateValueType, opt?: any) => unknown;
  setAttribute: <U extends keyof T>(attr: U, style: StyleConvert<T[U]>, state?: StateValueType, level?: number) => void;
  // 图元样式
  setStyle: (style: Partial<IMarkStyle<T>>, state?: StateValueType, level?: number) => void;
  initStyleWithSpec: (spec: any, key?: string) => void;
}

// IMarkOption 用于初始化和管理图元的上下文、全局映射等信息。
export interface IMarkOption extends ICompilableMarkOption {
  model: IModel;
  map: Map<StringOrNumber, IModel | IMark>;
  // 全局映射
  globalScale: IGlobalScale;
  // 关联系列编号
  seriesId?: number;
  // 组件图元具体类型
  componentType?: string;
  attributeContext?: IModelMarkAttributeContext;
}
    

  • 具体图元实现:
  • 代表文件:line.ts / symbol.ts / arc-3d.ts

  • 定义每种图元额外的配置和操作,一般都需要实现:

protected _getDefaultStyle() {
  const defaultStyle: IMarkStyle<IGroupMarkSpec> = {
    ...super._getDefaultStyle()
    // 图元特有的配置
  };
  return defaultStyle;
}    

例如,对于line图元,增加了_getIgnoreAttributes方法获取屏蔽的属性:

protected _getIgnoreAttributes(): string[] {
  if (this.model?.type === SeriesTypeEnum.radar && this.model?.coordinate === *'polar'*) {
    return [];
  }
  return [*'fill'*, *'fillOpacity'*];
}    

  • 图元集合管理 (mark-set)
  • 核心文件:index.ts

  • 主要功能:

  • 提供对多个图元实例的统一管理,支持增删查改操作;

  • 支持通过名称、类型、ID、附加信息等不同维度查找图元;

  • 存储与 Mark 关联的元信息(IMarkInfo),用于样式控制和业务逻辑判断

export class MarkSet {
  protected _children: IMark[] = [];  // 存储所有 Mark 实例
  protected _markNameMap: Record<string, IMark> = {}; // 名称索引
  protected readonly _infoMap = new Map<IMark, IMarkInfo>(); // Mark 附加信息
  // 添加 Mark 并关联信息
  addMark(mark?: IMark, markInfo?: IMarkInfo) {
    this._children.push(mark);
    this._markNameMap[mark.name] = mark;
    this._infoMap.set(mark, merge({}, MarkSet.defaultMarkInfo, markInfo));
  }
  // 按类型过滤 Mark
  getMarksInType(type: string | string[]): IMark[] {
    const types = array(type);
    return this._children.filter(m => types.includes(m.type));
  }
  // 按自定义信息查找 Mark
  getMarkWithInfo(info: Partial<IMarkInfo>) {
    return this._children.find(mark => {
      return Object.keys(info).every(key => info[key] === this._infoMap.get(mark)[key]);
    });
  }
  // 其他核心方法
  removeMark() { /* 删除逻辑 */ }
  clear() { /* 清空集合 */ }
  get() { /* 多方式获取 Mark */ }
}    

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

玄魂