基本概念
在 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 */ } }