顶层对象
G的初始化是通过创建一块Canvas画布,将创建的基础图形挂载到画布下,如下示例:
// 引入 G 核心中的画布、各种 2D/3D 图形
import { Canvas, Circle } from '@antv/g';
// 选择一个或多个 Renderer
import { Renderer } from '@antv/g-canvas';
// 使用 World API 描述场景
const canvas = new Canvas({ renderer: new Renderer() });
const circle = new Circle({ attrs: { r: 20 } });
canvas.appendChild(circle);
Canvas类似其他库中的Stage,作为入口。
Renderer则为渲染时所使用的渲染器,如2d/3d/svg,渲染器负责执行拾取、DOM事件、核心渲染过程等
基础图形
G的图形对象基类是DisplayObject,同pixi、fabric、zrender等,与konva不同的是,其他图形对象例如 Group Circle Text 等都会继承它
而G为了兼容 DOM Element API,使用下面的继承机制:
DisplayObject -> Element -> Node-> EventTarget -> EventEmitter
这个图形基类的继承设计并没有下面pixi的直观:
DisplayObject -> EventEmitter
Canvas执行机制
g-lite/src/Canvas.ts
class Canvas {
constructor() {
// ...
this.initRenderer(renderer);
// ...
}
initRenderer(renderer) {
// reset
this.inited = false;
this.readyPromise = undefined;
// 初始化rBush树,用法参考:https://www.5axxw.com/wiki/content/7wjc4t
this.context.rBushRoot = new RBush<RBushNodeAABB>();
// 装载四种插件,插件内apply方法中的逻辑会注册到tapable的各个生命周期中
this.context.renderingPlugins = [];
this.context.renderingPlugins.push(
new EventPlugin(),
new PrepareRendererPlugin(),
new DirtyCheckPlugin(),
new CullingPlugin([new FrustumCullingStrategy()]),
);
// 调用三种渲染器其中一种的init方法,这里最终会调用到g-plugin-canvas-renderer/src/index.ts下的init方法
// 到g-plugin-canvas-renderer之前的流程进本方法查看
this.loadRendererContainerModule(renderer);
// 创建canvas元素并初始化context上下文
// 最终会调用到g-canvas/src/Canvas2DContextService.ts下
// 调用过程:
// 由于上面的loadRendererContainerModule方法会先执行g-canvas/src/index.ts
// 这个文件中的this.registerPlugin(new ContextRegisterPlugin())会定位到Canvas2DContextService西安
this.context.contextService = new this.context.ContextService({
...runtime,
...this.context,
});
// 实例化渲染服务,最终执行到g-lite/src/service/RenderingService.ts
// RenderingService负责实例化并存储tapable的生命周期
// tapable:为webpack使用的生命周期库, .tap()等方法为注册一个周期, .call()/.promise()方法为执行注册的周期
this.context.renderingService = new RenderingService(runtime, this.context);
// 初始化DOM事件服务,最终执行到g-lite/src/service/EventService.ts下的init方法
this.context.eventService = new EventService(runtime, this.context);
this.context.eventService.init();
// 执行g-canvas/src/Canvas2DContextService.ts下的init方法
await this.context.contextService.init();
// 执行g-lite/src/service/RenderingService.ts下的init方法
await this.context.renderingService.init();
this.inited = true;
// getRoot方法中获取的this.document.documentElement类似konva中Stage下挂载的children
// 将根下挂载所有节点的渲染包围盒、几何包围盒、图形本身的脏标记置为true
// 这里重置的是渲染包围盒、几何包围盒的全局边界脏标记
this.getRoot().forEach((node) => {
const renderable = (node as Element).renderable;
if (renderable) {
renderable.renderBoundsDirty = true;
renderable.boundsDirty = true;
renderable.dirty = true;
}
});
// 触发mount生命周期
this.mountChildren(this.getRoot());
// 若配置开启自动渲染,执行首次渲染流程(在Canvas::render中会执行renderingService.render)
// renderingService.render是每一次渲染流程的汇入口,有点类似konva的_requestDraw
if (renderer.getConfig().enableAutoRendering) {
this.run();
}
}
private loadRendererContainerModule(renderer: IRenderer) {
/**
* import { Renderer } from '@antv/g-canvas';
* const canvas = new Canvas({ renderer: new Renderer() });
* 执行过程如上
传入的renderer参数即g-canvas/src/index.ts暴露出的CanvasRenderer类,代码如下:
* export * as CanvasRenderer from '@antv/g-plugin-canvas-renderer';
* 随后执行CanvasRenderer的init方法
* 这个init方法负责派发执行具体图形对象的样式绘制过程(填充、描边、阴影等)
*/
const plugins = renderer.getPlugins();
plugins.forEach((plugin) => {
plugin.context = this.context;
plugin.init();
});
}
}