前言
最近一段时间一直在学习可视化相关的知识,除了理论知识外,目前实际业务中最常用的可视化工具也需要加强学习,就比如Bizcharts、Bizcharts依赖的的G2,但是说到最核心的一层,还是得回到G绘图引擎,G绘图引擎也是G2、G6所依赖的基础,其中也对外暴露了许多来自G引擎的Api,因此熟悉好G的使用,对于掌握其他可视化库都是很有帮助的。
G源码解读
我这里学习的是G 3.5-prepare版本源码,也是3.0版本G中首先使用TS构建的版本,便于阅读。以下是我构建的源码中主要类的UML图,可以发现源码中使用了非常多继承关系,个中关系还是比较清晰的。
核心用法解读
Element元素
首先是Element,从上面的UML图可以看出的,Element类是Shape、Canvas、以及Group的共同父类,从这里其实可以得出,文档中指的Element是一个虚拟概念,即Shape、Group以及他们对应的子类Line、Rect、Canvas等,都是属于Element,那么Element的属性和方法在以上类的实例上都可以进行调用。
Container 容器
Container这个概念在文档中并没有详细的描述,但阅读源码不难发现,Container的TS接口定义如下:
export interface IContainer extends IElement {
...
很明显了,Container本质上也是Element元素,但Container其实是一种抽象的描述,落实到具体,属于Container类型的有两种元素
-
Canvas实例,也就是使用new Canvas()构建出来的最外层Canvas容器对象,它属于Container
export interface ICanvas extends IContainer { ... }
-
addGroup方法的放回对象,addGroup()方法会返回一个Group类型对象,该Group对象的Ts接口定义如下
export interface IGroup extends IElement, IContainer { /** * 是否是实体分组,即对应实际的渲染元素 * @return {boolean} 是否是实体分组 */ isEntityGroup(): boolean; }
可以看到,Group类型对象是属于Container的,同时也是一种Element。
Canvas 画布
Canvas 画布,这个概念比较好理解,也就是上图中的Canvas类,在实际开发中,创建一个Canvas类的实例也是进行绘图的第一步,同时Canvas方法会根据render属性的配置,决定是使用canvas或者svg来进行绘图。
export type Renderer = 'canvas' | 'svg';
每创建一个独立的图表,就会创建一个独立的Canvas实例
const chart = new Canvas({
container: 'container',
width: 600,
height: 500,
});
Group 图形分组
Group在上图的UML图中也就是Group类,可以看到,Group也是Element类型。然后Group对象的Ts接口定义如下:
export interface IGroup extends IElement, IContainer {
/**
* 是否是实体分组,即对应实际的渲染元素
* @return {boolean} 是否是实体分组
*/
isEntityGroup(): boolean;
}
可以看到,Group对象继承了Elemnt以及Container的类型定义,所以文档中对Group的使用也是非常直接了当,直接让我们使用Element和Container的属性与方法即可。
除此之外,额外定义了一个方法isEntityGroup,这个方法的实现其实很简单,如果是使用SVG渲染模式,那么会返回true,因为在Svg中,会使用实体标签 来包裹一个分组,相反若使用Canvas模式,那么会直接返回false,因为Canvas中并没有实际的图形分组概念。
在G2中,Group的最直接用处就是用在初始化一个图表的时候,一共会先创建3个Group,然后对其进行叠加,形成最终的图表
// 调用 view 的创建
super({
parent: null,
canvas,
// create 3 group layers for views.
backgroundGroup: canvas.addGroup({ zIndex: GROUP_Z_INDEX.BG }),
// 图表最后面的容器
middleGroup: canvas.addGroup({ zIndex: GROUP_Z_INDEX.MID }),
// 图表所在的容器
foregroundGroup: canvas.addGroup({ zIndex: GROUP_Z_INDEX.FORE }),
// 图表前面的容器
padding,
appendPadding,
visible,
options,
limitInPlot,
theme,
syncViewPadding,
});
在G2中使用registerShape方法注册自定义图形时,一般也是在已有Container上使用addGroup方法添加新分组,并在新分组中添加图形,最后将新Group返回进行叠加。
// 自定义Shape 部分
registerShape('point', 'pointer', {
draw(cfg, container) {
const group = container.addGroup({});
// 获取极坐标系下画布中心点
const center = this.parsePoint({ x: 0, y: 0 });
// 绘制指针
group.addShape('line', {
attrs: {
x1: center.x,
y1: center.y,
x2: cfg.x,
y2: cfg.y,
stroke: cfg.color,
lineWidth: 5,
lineCap: 'round',
},
});
group.addShape('circle', {
attrs: {
x: center.x,
y: center.y,
r: 9.75,
stroke: cfg.color,
lineWidth: 4.5,
fill: '#fff',
},
});
return group;
},
});
Shape 图形
Shape在G中也就是Shape类,继承Shape,形成了Line、circle、Ellipse等基础图形。
使用方法:
group.addShape('circle', {
attrs: {
x: 300,
y: 200,
r: 100,
fill: '#1890FF',
stroke: '#F04864',
lineWidth: 4,
radius: 8,
},
});