但愿读完系列文章后,可以对 G6 的功能分布、主要流程有大概的认知。 关注点在 G6 的介绍和主要流程,不涉及到具体 api 的使用。node
下面各个维度解释下G6git
G6 是一个图可视化引擎。它提供了图的绘制、布局、分析、交互、动画等图可视化的基础能力。旨在让关系变得透明,简单。让用户得到关系数据的 Insight。github
G6 是 antv 体系的一个图可视化品牌,主要关注关系图的绘制,antv 还有其余应用,好比关注图表的 G二、F2,关注地理数据渲染的 L7 等算法
G6 是一个开源的 JavaScript 图形库,能够支持 PC、移动端、小程序多个平台。npm
更多G6使用的示例参见图可视化引擎 G6json
G6 依赖渲染引擎 G 提供渲染能力,G 是一款Antv体系开源的支持 canvas、svg 渲染,跨 PC、mobile 平台的强大渲染引擎。 Graphin是基于 G6 的 React 库,经过接入 React 生态,使用起来更加简单。bootstrap
上图是官方 v4 的框架图,比较能看出 G6 基础的功能分布。canvas
G6 是一个多项目共存的库,使用monorepo的思想来管理项目。monorepo不作过多介绍。思路就是把相关联的项目都放在一个仓库里管理,包括开发、构建、发布等。我本身经验来看,这种模式确实在同时多个项目开发时有优点,免去管理、link 多个项目的麻烦。不过上手阶段我常常会有疑惑,不能确认问题出在哪一个项目,要总体build,苦笑,应该是我太菜。G6 使用 lerna 来管理全部的子项目,目录结构和简介以下:小程序
G6
|-packages
|--core // G6核心库,实现了大部分功能,主流程、动画、交互、状态管理
|--pc // 扩展core,提供更多的交互,pc的事件等
|--mobile // 扩展core,提供mobile、小程序平台的支持,移动端事件支持等
|--element // 提供预制的自定义图元素(节点、边、combo)
|--plugin // 插件,好比[鱼眼效果](https://g6.antv.vision/zh/examples/tool/fisheye#fisheye)
|--...
复制代码
构建项目:npm install
, npm run bootstrap
,更多指令能够看package.json文件api
提供渲染能力,向上提供
本小节简单描述了G的流程和输入的数据结构,用来辅助理解G6。一些更细的点,好比局部渲染、形状拾取、碰撞检测、坐标系统等,就暂时无论。从流程图能够看出,G 跑起来须要 Group 树结构,因此接下来 G6 须要作的工做就是构建这样的结构,交给 G 来渲染。
G6的核心概念 在官方文档有讲,此处再也不赘述,能够先创建下概念和基础用法。一个简单的示例以下:
const data = {
nodes: [
{
id: "node1",
label: "Circle1",
x: 150,
y: 150
},
{
id: "node2",
label: "Circle2",
x: 400,
y: 150
}
],
edges: [
{
source: "node1",
target: "node2"
}
]
};
const graph = new G6.Graph({
container: "container",
width: 500,
height: 500,
});
graph.data(data);
graph.render();
复制代码
例子简单,但也能基本说明问题。能够看出:
在此基础上有交互(behavior)、事件、状态管理、动画、布局几大功能。下面就按照G6的执行顺序,分析下G6的基础流程和相关设计。
建立graph实例后,同时会初始化如下单例,管理不一样的功能:
graph.cfg.canvas
G 对应的 Canvas 类实例,调控整个渲染流程,经过该单例与 G 对接起来
graph.cfg.itemController
管理 Item 实例,Item 是 G6 包装的节点类,输入的边和节点数据会组织成 item 实例数组。这个模块打通从数据到最终渲染的整个流程,下面的章节会着重分析这个模块相关的部分。
graph.cfg.layoutController
控制布局相关逻辑,布局算法比较复杂,G6 拆出了一个 npm 包(@antv/layout)专门放布局相关算法。
graph.cfg.viewController
控制显示相关逻辑。核心逻辑是计算视口居中,提供坐标转换等功能。
graph.cfg.eventController
控制事件相关逻辑,核心逻辑是拾取 G 传来的事件,出发用户定义事件,最终反馈到 item 上。
graph.cfg.modeController
控制交互,管理 behavior。
ShapeFactory
图元素的工厂函数,管理(crud、函数调用)定义的节点、边、combo。
下图展现了一个节点数据(参照前置文档示例中节点的数据结构)从输入,到最终渲染,数据的流动状态,以及经历的流程。
data输入后,须要依靠itemController建立出Item实例来管理。Item做为G6与渲染层的桥接,能够仔细分析下。Item类设计以下:
前面里串通了绘制流程,已经能画出了图。除此以外,做为图可视化引擎的G6,还支持了哪些功能,又怎么实现的呢?
本文总体的思路是,分析这些功能互相的关系、内部的一些流程、设计 、实现点,这样基本能从大到小,解释清楚整个系统是怎么运行的。
各个功能的分层关系以下:
整个G6就这些核心功能,从图中能够看出各个功能之间的层次关系,能了解到功能的所处位置。有了大方向上的认知后,能减小些迷茫。
这些功能除了动画,均有对应的Controller,由Graph封装并放在Graph.cfg上,并对外提供入口。G6有个设计的特色,就是全部状态相关的都会放在cfg里,表明的应该是这个类型的model。好比Graph类中的cfg,Item类中的Cfg等。不过把Controller这种逻辑密集的也挂在上面总感受有些奇怪。
事件、动画、Item节点管理会直接依赖G。而交互、状态管理、布局则更多基于G6本身的封装。
官方文档对事件有清晰的分类。总共分为三类事件:画布层次的事件(canvas:mousedown ...),节点层次的事件(node:click ...),按时机的事件(afterlayout ...)。事件包括事件名,以及事件的回调。事件名在不一样平台会有差别,好比pc平台的mouse事件,移动平台的touch事件。详细介绍见文档中核心概念章节的事件小节。
事件咱们都比较熟悉,观察者、发布订阅之类。G6事件也不例外,有个全局的事件中心,graph.cfg.eventController,提供事件的订阅和发布。而与咱们认识的Dom的事件不一样的是,G6面对的可能只有一个Dom元素,Canvas,因此不能依赖平台自己解析事件触发的节点、事件对象,须要本身实现及封装。
经过上篇文章,咱们知道,G将绘图的节点组织成group树结构,shape是树的子节点。要肯定到底点击到哪一个shape/group,思路其实显而易见就是遍历这棵树,找到节点便可。下面是quickHit模式下的节点拾取流程,源码参见G项目中g-base包里event.ts文件。(quickHit模式经看源码,主要是忽略了group的点击检测,只检测叶子节点,经过这样处理提高性能。)
其中点击检测每一个shape类型有实现本身的检测方法,好比圆使用点到圆心的距离等。每次触发事件都要遍历整颗树,作点击检测,是个性能点,所以G自己也作了一些缓存处理。
事件冒泡是G作的,比较简单,核心思路是检测命中shape后,依次向上触发parent的事件,直到事件对象里的 propagationStopped
属性为true或者到达根元素。
**交互模式解决的问题?**是用来批量操做用户交互行为,好比切换编辑和查看模式这种场景。详细介绍见文档中核心概念章节的Behavior相关小节。 类设计:
Behavior是个工厂类,使用对像存储各个Behavior类型。每一种Behavior子类型,定义了所需的事件行为集合,提供事件的绑定与解绑。 ModeController,根据输入的mode配置(Behavior类型的索引组成的数组),或动态切换不一样的Behavior子类实例。
ModeControler绑定Behavior流程:
核心思路就是ModeController调用Behavior工厂建立子类型实例,实例完成绑定过程。触发时机是初始化Graph或系统运行期间动态调用Graph.setMode。
状态管理解决的问题? 实现交互时或者业务逻辑中节点状态变化后,触发节点样式的更新或其余自定义行为。这里的状态能够是交互中的hover、active,也能够是自定义的running任何一种状态。对状态的响应若是只是样式变化,能够直接在建立Graph实例时输入的节点配置中设置。若是其余更加复杂的行为,好比加个动画,就须要自定义节点,复写默认的setState方法。详细介绍见文档中核心概念章节的交互mode、状态state小节。
类设计:
Graph初始化后会实例化StateController和ItemController,能够在Graph实例的cfg中访问到。
StateController目前看来只是维护了各类状态下的Item数组,以及更新状态后的事件触发,不影响整个state的流程。
设置状态流程:
动画G6没有作什么处理,直接使用的G的动画能力。根据使用场景,分为全局动画和自定义节点动画两部分。详细介绍见文档中核心概念章节的基础动画小节。 **全局动画:**拿到节点树根部元素,调用animate接口;
自定义节点动画:在自定义图元素(能够看上篇了解图元素,G6用来对接G,封装了具体的shape)时,拿到shape或group节点,调用animate接口便可。
类设计:
动画流程:
核心思路就是Element获取Canvas中的TimeLine实例,传入动画数据,交给d3-timer去逐帧执行,经过插值实现连续的动画效果。
插值计算使用了d3-ease、d3-interpolate辅助计算。
更新到画布是Element的能力。
触发流程时机是全局动画或自定义动画调用animate时。
布局是采用算法,使节点和边可以以某种方式分布。分为通常图布局和树图布局,目前我只看到了通常图布局,就只分析通常图布局。详细介绍见文档中核心概念章节的图布局小节。
相关的类:
布局使用在初始化Graph时建立的LayoutController控制。具体的布局算法来自npm包** @antv/layout** 。
通常图布局的具体流程
能够看出,布局和Graph经过数据解耦。Graph面向数据,数据正确就能正常渲染。布局算法也只用关注对数据的生产,不用关注Graph或者其余系统。
布局算法对节点坐标更新以达到布局的目的,其余数据项也能够更新,不过就显得职责不清晰,因此不建议布局算法对其余数据项的更新。
PC和Mobile平台布局的流程不一致。PC会额外支持worker布局,就是图中第三列的布局流程,把布局拆成了三个步骤异步通知进行。
触发流程的时机是在初始化或者切换数据、布局的时候,具体是在调用Graph的render/changeData/updateLayout时。
这里是一个自定义布局的例子,看完这个例子应该会对布局算法的职责有更清晰的认知。
经过对各个功能的层次分布,以及各自实现流程的分析,应该对G6实现思路有了大体的认知。有兴趣的话,能够对着流程看看源码,看看具体某个步骤的实现逻辑。通过这段时间G6的梳理,对G6也有了更多的了解,最后感谢阅读。
微信搜索公众号Eval Studio,关注更多动态。