G2 5.0 底层将根据一套规范(Specification)去渲染图表,同时暴露尽量兼容 G2 4.0 的 API。所以 G2 的架构有以下两个核心模块。
- runtime: 根据指定的配置去渲染图表。
- api:基于 runtime 封装兼容 G2 4.0 的 API。
runtime 的主要任务就是把输入的 JavaScript Object 转换成一些列图形。为了代码的可读性、鲁棒性、可扩展性、易测试性,将整个转换过程拆分成一系列函数,每个函数对应一个组件。每个组件的首字母大写,返回一个函数,数组或者对象,同时每个组件具有 props 去提供在 runtime 里面使用的一些信息。
export type G2ComponentProps = {
async?: boolean;
[key: string]: any;
};
export type G2Component<R extends Function | object | any[], O = {}> = {
(options: O): R;
props: G2ComponentProps;
};
// linear scale
type ScaleOptions = {
domain: number[],
range: any[],
};
export const Scale: ScaleComponent<ScaleOptions> = (options) => {
const {domain, range} = options;
const [d0, d1] = domain;
const [r0, r1] = range;
return x => {
const t = (x - d0) / (d1 - d0);
return r0 * (1 - t) + r1 * t;
}
}
Scale.props = {
quantitative: true,
}
按照功能的不同将组建分为了以下种类,其中每一类都是一个独立的模块,它们会在 runtime 里面被串联起来使用。
- transform:数据预处理组件,将任意类型数据转换成扁平的数据。
- encode:提取通道值组件,从原始数据中提取通道值。
- statistic:通道值处理组件,新增或者改变通道的值。
- scale:通道映射组件,将数据映射为图形元素的通道。
- infer:推断配置组件,简单根据数据和配置推断一些确实的配置。
- palette:颜色通道使用的色板,返回用于比例尺值域的颜色。(需要增强分析的参与)
- coordinate:坐标系变换组件,将归一化的点转换成画布坐标系的点。
- component:辅助组件,一系列提高信息传递效率辅助。
- geometry:绘制图形组件,将处理好的数据绘制到画布上。
- shape: 计算几何图形的形组件,根据坐标数据和坐标系给 selection 添加形状相关的信息。
- adjust:调整位置通道组件,根据容器大小和比例尺去调整位置坐标。
- annotation:标注图表的关键信息组件,基于 geometry 和 shape 封装的有标注能力的视觉元素。
- animation:视觉元素动画组件,一系列定义好视觉动画。(需要设计师的参与)
- action:事件处理组件,用于修改图表状态。
- interaction: 应用交互组件,基于 trigger 和 action 封装一系列交互。
- theme:图表主题组件,用于改变图表的整体样式。
- composition: 视图复合组件。
- renderer:使用的渲染器,可以在不同端,渲染不同风格的图表。
最后会有一个 stdlib 模块将 G2 5.0 内置的标准组件导出。
- stdlib:导出一些 G2 默认使用的组件。
还有一个 shared 文件夹放一些公有的函数。
- shared:stdlib 和 runtime 都会使用的函数。
Runtime
G2.createRuntime()
根据指定的 renderOptions 创建一个新的渲染环境,默认使用内置的标准库 builtins。G2.createRuntime(rendererOptions[,builtins=createLibrary()])
renderOptions 是渲染器相关的配置,如果是客户端(在浏览器中运行)的渲染器,具有 container 等属性;如果是在服务端(在 Node 中运行)的渲染器则需要其他属性。默认的渲染环境的种类是 canvas。
import { createRuntime } from '@antv/g2';
const runtime = createRuntime({container: 'chart'});
const data = [
{ genre: 'Sports', sold: 275 },
{ genre: 'Strategy', sold: 115 },
{ genre: 'Action', sold: 120 },
{ genre: 'Shooter', sold: 350 },
{ genre: 'Other', sold: 150 },
];
runtime.render({
type: 'inteval',
data,
encode: {
x: 'genre',
y: 'sold',
}
});
// 目标支持以下种类的渲染器
type G2RenderOptions = {
type: 'canvas' | 'svg' | 'canvas-sketchy' | 'svg-sketchy' | 'server'
}
如果指定 builtins,该对象的每一个属性定义了一个在渲染过程中使用的组件。如果没有指定 builtins,将默认使用 createLibrary() 返回的内置标准库。同时可以定义一个新的组件,或者覆盖一个存在的。
import { createRuntime, createLibrary } from '@antv/g2';
// 定义一个新的 Geometry
const Link = () => {};
Link.props = {type: 'link'};
// 扩展标准库
const library = Object.assign(createLibrary(), {'geometry.link': Link});
const runtime = createRuntime({container}, library);
// 绘制
runtime.render({
type: 'link', // 使用定义的 link 几何图形
});
这里有几点需要说明:
- rendererOptions 为什么不做为图表描述(Options)的一部分:希望图表的描述是和渲染环境无关的,这样在不同渲染环境里面不需要更改图表的描述,去需要修改运行是的渲染参数即可。
- 为什么不使用 new Runtime 而使用 createRuntime:不把对象直接暴露给用户,防止用户直接继承这个对象,在之后的升级过程中产生冲突。后面的 createLibrary 同理。
- 为什么使用两个参数,而不是合并成一个参数,比如
createRuntime(options)
: renderOptions 是必选的,builtins 是可选的,分开可以让函数签名更加简洁。Runtime.render()
根据指定的配置项去渲染图表,所有的选项都是可选的。Runtime.render(options)
该函数的返回值由渲染器的选项决定。如果是客户端的渲染器,那么应该返回 container 节点;如果是服务端的渲染器,应该返回渲染出来的图片数据或者 Lottie 文件。
import { createRuntime } from '@antv/g2';
const data = [
{ genre: 'Sports', sold: 275 },
{ genre: 'Strategy', sold: 115 },
{ genre: 'Action', sold: 120 },
{ genre: 'Shooter', sold: 350 },
{ genre: 'Other', sold: 150 },
];
const runtime = createRuntime({container: 'chart'});
runtime.render({
type: 'inteval',
data,
encode: {
x: 'genre',
y: 'sold',
}
});
Runtime.dispose()
Runtime.dispose();
销毁内部创建的实例。如果是在客户端的运行环境,这意味着还要销毁定时器,取消事件的监听等等。
Stdlib
G2.createLibrary()
G2.createLibrary()
创建一个 runtime 使用的标准库,该标准库是一个平铺的对象。该对象的每一个属性是一个函数,对应一个组件。
import { createLibrary } from '@antv/g2';
createLibrary();
/*
{
'scale.linear': Linear,
'geometry.point': Point,
...
}
*/
API
G2.Chart()
Todo
Chart.*()
Todo
Transform
数据预处理组件,将任意类型数据转换成扁平的数据。将之前 DataSet 的部分能力迁移过来。
export type TransformFunction = (data?: any) => any;
export type TransformComponent<O = {}> = G2Component<TransformFunction, O>;
import {TransformComponent as TC} from '../runtime';
export type SortOptions = {
callback?: (a: any, b: any) => boolean;
};
export const Sort: TC<SortOptions> = (options) => {
const {callback = (a, b) => a - b} = options;
return data => data.sort(callback);
}
Sort.props = {};
- Connector
- fetch
- Transform
- sortBy
- filterBy
- Layout
- force graph
- tree graph
- circle packing
- treemap
- word cloud
- sankey
Encode
提取通道值组件,从原始数据中提取通道值。对应图形语法中的 Data 操作,目前只内置4个比较常用的,之后可以扩展。(比如针对科学可视化等)
```typescript import {EncodeComponent as EC} from ‘../runtime’;export type EncodeFunction = (data?: any) => any[];
export type EncodeComponent<O = {}> = G2Component<EncodeFunction, O>;
export type ConstantOptions = { value?: any; };
export const Constant: EC
Constant.props = {};
- constant
- field
- transform
- statistic
<a name="yme2S"></a>
# Statistic
通道值处理组件,新增或者改变通道的值。
```typescript
export type StatisticValue = { index: number[]; value: any[] };
export type StatisticFunction = (value?: StatisticValue) => StatisticValue;
export type StatisticComponent<O = {}> = G2Component<StatisticFunction, O>;
import {StatisticComponent as SC} from "../runtime";
export type SelectLastOptions = {};
export const SelectLast: SC<SelectLastOptions> = (options) => {
return ({index, value}) => ({
index: index[index.length - 1],
value
})
}
SelectLast.props = {};
Scale
通道映射组件,将数据映射为图形元素的通道。
export type Scale = any;
export type ScaleComponent<O = {}> = G2Component<Scale, O>;
import {Linear as LinearScale, LinearOptions} from '@antv/scale';
import {ScaleFuntion as SF} from '../runtime';
export type LinearOptions;
export const Linear: SF<LinearOptions> = (options) => {
const scale = new Linear(options);
return scale;
}
Linear.props = {};
Infer
推断配置组件,简单根据数据和配置推断一些配置。
export type InferFunction = (encodes: Record<string, Encode>) => Record<string, Encode>;
export type InferComponent<O = {}> = G2Component<InferFunction, O>;
import {InferComponent as IC} from '../runtime';
export type MaybeZeroY2Options = {};
export const MaybeZeroY2: IC<MaybeZeroY2Options> = (options) => {
return (encodes) => {
const {y: Y, ...rest} = encodes;
if (Array.isArray(Y[0]) || Y[0].length >= 2) {
return encodes;
}
return {y: Y.map(d => [[d].flat(Infinity), 0]), ...rest};
};
};
MaybeZeroY2.props = {};
Palette
颜色通道使用的色板,返回用于比例尺值域的颜色。每一个主题都有一组默认的色板。
export type Palette = string[];
export type PaletteComponent<O = {}> = G2Component<Palette, O>;
import {PaletteComponent as PC} from "../runtime";
export type AntVPaletteOptions = {};
export const AntVPalette: PC<AntVPaletteOptions> = (options) => {
return ['red', 'yellow', 'green', 'steelblue'];
}
AntVPalette.props = {};
Coordinate
坐标系变换组件,将归一化的点转换成画布坐标系的点。除了 @antv/coord 能力之外,还会具备 d3-geo 的一些地理坐标系的能力。
export type CoordinateFunction = (p: [number, number]) => [number, number];
export type CoordinateComponent<O = {}> = G2Component<CoordinateFunction, O>;
import {CoordinateComponent as CC} from "../runtime";
export PolarOptions = {};
export const Polar: CC<PolarOptions> = (options) => {
const {startAngle, endAngle, innerRadius, outerRadius} = options;
return ['polar', startAngle, endAngle, innerRadius, outerRadius];
};
Polar.props = {};
Component
辅助组件,返回一个绘制组件的函数。
export type ComponentFunction = (
scale: Record<ChannelTypes, Scale>,
dimensions: {x?:number, y?:number, width?:number, height?:number},
coordinate: Coordinate
) => GShape;
export type ComponentComponent<O = {}> = G2Component<ComponentFunction, O>;
import { Continuous } from '@antv/gui';
import { ComponentComponent as CC } from '../runtime';
export ContinuousOptions = {};
export const ContinuousLegend: CC<ContinuousOptions> = (options) => {
return (scale, dimensions, coordinate) => {
const {x, y, width, height} = dimensions;
const {domain} = scale;
return new Continuous({
color: domain,
x, y, width, height,
});
}
}
ContinuousLegend.props = {};
Geometry
绘制图形组件,确定几何元素的绘制需要的点。
export type GeometryFunction = (
index: number[],
scale: Record<ChannelTypes, Scale>,
value: Record<ChannelTypes, any[]>,
style: Record<string, string>,
coordinate: Coordiante,
) => [number, nnumber][];
export type GeometryComponent<O = {}> = G2Component<GeometryFunction, O>;
import {GeometryComponent as GC} from '../runtime';
import {key as k} from '../utils';
export type PointGeometryProps = {};
export const PointGeometry: GC<PointGeometryProps> = (options) => {
return (index, scale, value, styles, coordinate) => {
const {x:X, y:Y, size:R} = value;
const [width, height] = coordinate.getSize();
return index.map(i => {
const r = R[i] || styles.r;
const a = r / width;
const b = r / height;
const p1 = [x - a, y - b];
const p2 = [x + a, y + b];
return [p1, p2].map(coordinate.map);
});
}
}
PointGeometry.props = {};
Shape
计算几何图形的形组件,根据坐标数据和坐标系给 selection 添加形状相关的信息。
export type ShapeFunction = (
index: number[],
points: [number, number][],
value: Record<ChannelTypes, any[]>,
coordinate: Coordinate
) => any;
export type ShapeComponent<O = {}> = G2Component<ShapeFunction, O>;
import { Circle} from '@antv/g'
import { ShapeComponent as SC } from '../runtime';
export type PointOptions = {};
export const PointShape: SC<PointOptions> = () => {
return (index, points, value, styles, coordinate) => {
const {color: C} = value;
return Array.from(index, i => {
const [p1, p2] = points[i];
const [x1, y1] = p1;
const [x2, y2] = p2;
const x = (x1 + x2) / 2;
const y = (y1 + y2) / 2;
const r = Math.min(x2 - x1, y2 - y1);
const color = C[i];
return new Circle({
style: {
...styles,
r,
x,
y,
fill: color
}
});
});
}
}
PointShape.props = {
geometry: 'point',
}
Adjust
调整位置通道组件,根据容器大小和比例尺去调整位置坐标。
export type AdjustFunction = (
points: [number, number][],
coordinate: Coordinate,
path: Path[],
) => void;
export type AdjustComponent<O = {}> = G2Component<AdjustFunction, O>;
import { AdjustComponent as AC} from '../runtime';
export type PackOptions = {};
export const Pack: AC<PackOptions> = (options) => {
// @todo
return (points, coordinate, path) => {};
}
Pack.props = {};
Annotation
标注图表的关键信息组件,基于 geometry 和 shape 封装的有标注能力的视觉元素。这个也可以支持 shape。
export type AnnotationFunction = GeometryFunction;
export type AnnotationComponent<O = {}> = G2Component<AnnotationFunction, O>;
Animation
视觉元素动画组件,返回对应的动画描述。(参考:https://g-next.antv.vision/zh/docs/api/animation)
export type Transition = { attribute: string; from?: number; to?: number };
export type Animation = Transition[];
export type AnimationComponent<O = {}> = G2Component<Animation, O>;
import { AnimationComponent as AC } from '../runtime';
export type FadeInOptions = {};
export type FadeIn: AC<FadeInOptions> = (options) => {
return {
keyframes: [
{opacity: 0},
{opacity: 1}
],
options,
}
}
FadeIn.props = {};
Action
事件处理组件,用于修改图表状态。
export type ActionFunction = (event: Event, Store: store) => any;
export type ActionComponent<O = {}> = G2Component<ActionFunction, O>;
import {ActionComponent as AC} from '../runtime';
export type HighlightOptions = {};
export const Highlight: ActionComponent<HighlightOptions> = (options) => {
const { color } = options;
return (state, payload) => {
const { event } = payload;
const { id } = event.target;
const { style, ...rest } = state;
// 更新这个 id 对应元素的 state
style.set(id, { fill: color });
return {
...rest,
style
}
};
}
Highlight.props = {};
Interaction
应用交互组件,基于 trigger 和 action 封装一系列交互。
export type Interaction = {
showEnable?: any[];
start: any[];
processing?: any[];
end?: any[];
rollback?: any[];
};
export type InteractionComponent<O = {}> = G2Component<Interaction, O>;
import {InteractionComponent as IC} from '../runtime';
export type HighlightElementOptions = {};
export const HighlightElement: IC<HighlightElementOptions> = () => {
return {
start: {trigger: 'element:enter', action: 'highlight'}
}
}
HighlightElememt.props = {};
Theme
export type Theme = Record<string, string>;
export type ThemeComponent<O = {}> = G2Component<Theme, O>;
import { ThemeComponent } from '../runtime';
export type LightThemeOptions = {};
export const LightTheme = (options) => {
return {
...options,
background: 'black',
};
};
LightTheme.props = {};
Composition
Todo
Renderer
export type Renderer = any;
export type RendererComponent<O = {}> = G2Component<Renderer, O>;
import { Renderer } from '@antv/g-canvas';
import { RendererComponent as RC } from '../runtime';
export type CanvasOptions = {
container?: string;
};
export const Canvas: RendererComponent<CanvasOptions> = (options) => {
const renderer = new Renderer();
return new Canvas({renderer, ...options});
}
CanvasRenderer.props = {};