简介
视觉映射是数据与图像之间的桥梁,它将“数据模型”映射到“图像模型”,为不同类型的数据选择适合它的视觉变量。例如,我们使用柱状图表示一个班级中男生和女生的平均成绩,那么可以将数据中的性别属性映射到图像中的颜色属性,将数据中的成绩属性映射到图像中柱状图的高度(Y轴坐标)属性。下面我们通过一个简单的用例,分析数据是如何被映射到最终所看到的图 像的。
图元的映射流程
使用示例
const spec = { type: 'line', data: [ { id: 'lineData', values: [ { date: 'Monday', class: 'class No.1', score: 20 }, { date: 'Monday', class: 'class No.2', score: 30 }, { date: 'Tuesday', class: 'class No.1', score: 25 }, { date: 'Tuesday', class: 'class No.2', score: 28 } ] } ], seriesField: 'class', xField: 'date', yField: 'score', point: { style: { fill: 'blue' } } }; const vchart = new VChart(spec, { dom: CONTAINER_ID }); vchart.renderSync();
该示例创建了一个line类型的图元系列来展示4条数据,其中class属性相同的点将被连成一条折线,date属性被映射到X轴坐标,score属性被映射到Y轴坐标,效果如下:

图元的创建
下面我们通过代码,分析renderSync()中是如何解析配置spec,并生成图上的各种图元的。总体来说,renderSync()中包含以下三个阶段,渲染前、渲染、渲染后。
// packages/vchart/src/core/vchart.ts protected ***_renderSync*** = (option: IVChartRenderOption = {}) => { const self = this as unknown as IVChart; if (!this.***_beforeRender***(option)) { return self; } this._compiler?.***render***(option.morphConfig); this.***_afterRender***(); return self; };
其中渲染过程属于VGrammar的范畴,而渲染后主要进行动画状态的更新,我们主要关注与图元有关的渲染前准备,包括初始化图表配置、实例化图表、编译渲染指令。
1. 初始化图表配置

// packages/vchart/src/core/vchart.ts private ***_initChartSpec***(spec: any, actionSource: VChartRenderActionSource) { // 如果用户注册了函数,在配置中替换相应函数名为函数内容 if (VChart.***getFunctionList***() && VChart.***getFunctionList***().length) { spec = ***functionTransform***(spec, VChart); } this._spec = spec; // 创建图表配置转换器,并转换为common chart的配置 if (!this._chartSpecTransformer) { this._chartSpecTransformer = Factory.***createChartSpecTransformer***( this._spec.type, this.***_getChartOption***(this._spec.type) ); } this._chartSpecTransformer?.***transformSpec***(this._spec); // 转换模型配置 this._specInfo = this._chartSpecTransformer?.***transformModelSpec***(this._spec); }
首先第一步,将用户注册的函数名替换为相应的函数实体。之后,根据图表类型,创建相应的图表配置转换器,将该类型图表的配置转换为common类型图表的配置。这其中包括,根据图表类型创建默认series,补全用户定义的series配置:
// packages/vchart/src/chart/cartesian/cartesian-transformer.ts const defaultSeriesSpec = this.***_getDefaultSeriesSpec***(spec); if (!spec.series || spec.series.length === 0) { // 没有用户定义的系列 采用默认 spec.series = [defaultSeriesSpec]; } else { spec.series.***forEach***((s: ISeriesSpec) => { if (!this.***_isValidSeries***(s.type)) { // 判断用户定义系列是否有效 return; } Object.***keys***(defaultSeriesSpec).***forEach***(k => { // 补全配置 if (!(k in s)) { s[k] = defaultSeriesSpec[k]; } }); }); }