// 创建 G6 图实例 const graph = new G6.Graph({ container: 'mountNode', // 指定图画布的容器 id,与第 9 行的容器对应 // 画布宽高 width: 800, height: 500, }); // 读取数据 graph.data(data); // 渲染图 graph.render(); //监听 graph.on() |
Graph 对象的生命周期为:初始化 —> 加载数据 —> 渲染 —> 更新 —> 销毁。
上面代码中实例化 Graph 的部分使用了三个必要的配置项:
container
width
、height
renderer
fitView
fitViewPadding
fitCenter
defaultNode
defaultEdge
nodeStateStyles
edgeStateStyles
layout
modes
animate
animateCfg
plugins
每个图元素由图形(Shape) 组成,且都会有自己的唯一关键图形(keyShape)。
circle:圆;
rect:矩形;
ellipse:椭圆;
polygon:多边形;
fan:扇形;
image:图片;
marker:标记;
path:路径;
text:文本;
dom(svg):DOM
获取实例的属性值。
更新实例的单个绘图属性。
批量更新实例绘图属性。
addGroup(cfgs)
addShape(type, cfgs)
变换
获取当前矩阵:getMatrix();
设置矩阵:setMatrix(matrix) 或 attr('matrix', matrix);
重置矩阵:resetMatrix()。
图的元素(Item)包含图上的节点 Node 、边 Edge 和 Combo 三大类。
样式属性,通过 style
字段对象进行配置,和元素的关键图形相关,例如 fill
,stroke
。
其他属性,例如 id
、type
,不能在元素状态改变是进行改变,可通过 graph.updateItem 进行手动更新。
节点
G6 的内置节点包括 circle,rect,ellipse,diamond,triangle,star,image,modelRect。这些内置节点的默认样式分别如下图所示。
定义方式
// 1 defaultNode: { type: 'circle', // 其他配置 } //2 graph.node((node) => { return { id: node.id, type: 'rect', style: { fill: 'blue', }, }; }); graph.data(data); graph.render(); //3 const data = { nodes: [ { id: 'node_circle', x: 100, y: 100, type: 'circle', label: 'circle', },] } |
自定义
G6.registerNode( 'nodeName', { options: { style: {}, stateStyles: { hover: {}, selected: {}, }, }, /** * 绘制节点,包含文本 * @param {Object} cfg 节点的配置项 * @param {G.Group} group 图形分组,节点中图形对象的容器 * @return {G.Shape} 返回一个绘制的图形作为 keyShape,通过 node.get('keyShape') 可以获取。 * 关于 keyShape 可参考文档 核心概念-节点/边/Combo-图形 Shape 与 keyShape */ draw(cfg, group) {}, /** * 绘制后的附加操作,默认没有任何操作 * @param {Object} cfg 节点的配置项 * @param {G.Group} group 图形分组,节点中图形对象的容器 */ afterDraw(cfg, group) {}, /** * 更新节点,包含文本 * @override * @param {Object} cfg 节点的配置项 * @param {Node} node 节点 */ update(cfg, node) {}, /** * 更新节点后的操作,一般同 afterDraw 配合使用 * @override * @param {Object} cfg 节点的配置项 * @param {Node} node 节点 */ afterUpdate(cfg, node) {}, /** * 响应节点的状态变化。 * 在需要使用动画来响应状态变化时需要被复写,其他样式的响应参见下文提及的 [配置状态样式] 文档 * @param {String} name 状态名称 * @param {Object} value 状态值 * @param {Node} node 节点 */ setState(name, value, node) {}, /** * 获取锚点(相关边的连入点) * @param {Object} cfg 节点的配置项 * @return {Array|null} 锚点(相关边的连入点)的数组,如果为 null,则没有控制点 */ getAnchorPoints(cfg) {}, }, // 继承内置节点类型的名字,例如基类 'single-node',或 'circle', 'rect' 等 // 当不指定该参数则代表不继承任何内置节点类型 extendedNodeName, ); |
连接方式
// 接入点 anchorPoints: [ [0, 1], [0.5, 1], ], |
jsx写法
<[group|shape] [key]="value" style={{ [key]: value }}> <[more tag] /> ... <text>value</text> </[group|shape]> |
边
line:直线,不支持控制点;
polyline:折线,支持多个控制点;
arc:圆弧线;
quadratic:二阶贝塞尔曲线;
cubic:三阶贝塞尔曲线;
cubic-vertical:垂直方向的三阶贝塞尔曲线,不考虑用户从外部传入的控制点;
cubic-horizontal:水平方向的三阶贝塞尔曲线,不考虑用户从外部传入的控制点;
loop:自环。
定义方式: 与节点类似
箭头:
//默认 style: { endArrow: true, startArrow: true } //内置 6种 endArrow: { // 使用内置箭头路径函数,参数为箭头的 宽度、长度、偏移量(默认为 0,与 d 对应) path: G6.Arrow.triangle(10, 20, 25), d: 25 } // 自定义箭头 // 只有内置箭头和自定义箭头可以配置样式 style: { endArrow: { path: 'M 0,0 L 20,10 L 20,-10 Z', d: 5, fill: '#f00', stroke: '#0f0', opacity: 0.5, lineWidth: 3, // ... }, } |
自定义:同节点
combo
G6 的内置 Combo 包括 circle 和 rect 两种类型
对于熟悉图可视化类库的用户来说,节点分组是非常实用的一个功能
G6 已经存在一个节点分组 Node Group 功能,但它的机制无法支持一些较复杂的功能,例如:带有节点分组的图布局、自定义 Combo、嵌套节点分组的均匀 padding、节点与分组的边、分组与分组的边、空的节点分组等
V3.5 推出了全新的节点分组 Combo 机制,能够支持所有常用功能
{ nodes: [ { id: 'node1', comboId: 'comboA' // node1 属于 comboA }, { id: 'node2', comboId: 'comboB' // node2 属于 comboB }, { id: 'node3' // node3 不属于任何 combo }, // ... ], edges: [ // ... ], combos: [ { // 定义 comboA id: 'comboA', label: 'A', parentId: 'comboC' }, { // 定义 comboB id: 'comboB', parentId: 'comboB' }, { // 定义 comboC,这是一个空的 combo id: 'comboC' }, // ... ] } |
其他内容:类似与节点
高级样式
背景
defaultNode: { position: 'left', style: { background: { fill: '#ffffff', stroke: 'green', padding: [3, 2, 3, 2], radius: 2, lineWidth: 3, }, }, |
三种方式更新文本样式
// 1. 实例化默认 defaultNode: { type: 'node', labelCfg: { style: { fill: '#fff', fontSize: 14, }, }, }, // 2.数据指定 const data = { nodes: [ { id: 'node1', label: 'node1', labelCfg: { style: { fill: '#fff', fontSize: 12, }, }, }, ], }; // 3.update/updateItem graph.updateItem(node, { // 节点的样式 style: { stroke: 'blue', }, // 节点上文本的样式 labelCfg: { style: { fill: '#fff', fontSize: 12, }, }, }); |
渐变色/纹理
操作
更新样式
更新节点边: 三种方式
层级
所有节点会绘制在所有边的上层
先绘制图形在后绘制图形后边
toFront()
与 toBack()
显示/隐藏
show()/hide()
多条边
自定义边 edgeType
锁定/解锁
lock()
、unlock()
和 hasLocked()
不可拖动
不可缩放
一般布局
Random Layout:随机布局;
Force Layout:G6 4.0 支持的经典力导向布局,支持 GPU 并行计算;
Force Layout:引用 d3 的经典力导向布局;
Fruchterman Layout:Fruchterman 布局,一种力导布局;
Circular Layout:环形布局;
Radial Layout:辐射状布局;
MDS Layout:高维数据降维算法布局;
Dagre Layout:层次布局;
Concentric Layout:同心圆布局;
Grid Layout:网格布局;
Combo Force Layout:V3.5 新增。适用于带有 combo 图的力导向布局,推荐有 combo 的图使用该布局。
树图
CompactBox Layout:紧凑树布局;
Dendrogram Layout:树状布局(叶子节点布局对齐到同一层);
Indented Layout:缩进布局;
Mindmap Layout:脑图布局。
布局切换
updateLayout(params)
:布局方法或参数的切换;
graph.updateLayout({ type: 'force', // 布局名称 preventOverlap: true, // 布局参数,是否允许重叠 nodeSize: 40, // 布局参数,节点大小,用于判断节点是否重叠 linkDistance: 100, // 布局参数,边长 }); |
changeData()
:数据的切换。
graph.changeData(data2); |
子图
子图布局独立与全局布局的思路,与 graph 不挂钩,直接使用实例化布局方法的方式,灌入子图数据,通过布局将位置写到相应数据中。这种机制还可供外部的全局布局使用,即使不用 G6 渲染,也可以计算节点布局后的位置
// 实例化布局 const subgraphLayout = new G6.Layout['force']({ center: [500, 450], }); // 初始化布局,灌入子图数据 subgraphLayout.init({ nodes: subGraphNodes, edges: subGraphEdges, }); // 执行布局 subgraphLayout.execute(); // 图实例根据数据更新节点位置 graph.positionsAnimate(); |
webworker
在大规模图可视化中,布局算法往往需要较大的计算量。
workerEnabled: true, // 开启 Web-Worker |
树图不支持 Web-Worker 机制;
子图布局机制暂不支持 Web-Worker 机制。
自定义布局
getDefaultCfg() { return {}; }, /** * 初始化 * @param {object} data 数据 */ init(data) {}, /** * 执行布局 */ execute() {}, /** * 根据传入的数据进行布局 * @param {object} data 数据 */ layout(data) {}, /** * 更新布局配置,但不执行布局 * @param {object} cfg 需要更新的配置项 */ updateCfg(cfg) {}, /** * 销毁 */ destroy() {}, }); |
布局预测
import { GraphLayoutPredict } from '@antv/vis-predict-engine' const { predictLayout, confidence } = await GraphLayoutPredict.predict(data); const graph = new G6.Graph({ // 省略其他配置 layout: { type: predictLayout } }) |
监听/绑定
画布、图形层次的事件,mousedown
,mouseup
,click
,mouseenter
,mouseleave
等;
节点/边 上的事件,node:mousedown
,edge:click
等,以 type:eventName
为事件名称;
时机事件:
节点/边增删改时的事件, 例如:beforeadditem
,afteradditem
等;
节点/边状态改变时的事件:beforerefreshitem
与 afterrefreshitem
;
布局时机:beforelayout
与 afterlayout
。
graph.on('click', (ev) => { const shape = ev.target; const item = ev.item; if (item) { const type = item.getType(); } }); graph.on('node:click', (ev) => { const shape = ev.target; const node = ev.item; }); |
内置 behavior
ehavior 是 G6 提供的定义图上交互事件的机制。
G6 目前共提供了以下 14 个内置的 Behavior。
drag-combo
collapse-expand-combo
drag-canvas
zoom-canvas
drag-node
click-select
tooltip
edge-tooltip
activate-relations
brush-select
lasso-select
collapse-expand
create-edge
shortcuts-call
在交互行为上, G6 主要考虑了三个场景:
展示关系数据;
可视化建模;
图分析。
在这些场景中只要用户可能无法一眼看清楚所有需要的信息,都需要进行交互,例如:
图太大,需要缩放;
单个节点上展示的信息太少,需要通过 tooltip 显示详情;
对节点进行增删改查。
default 模式中包含点击选中节点行为和拖拽画布行为;
edit 模式中包含点击节点弹出编辑框行为和拖拽节点行为。
modes: { // 支持的 behavior default: ['drag-canvas', 'zoom-canvas'], edit: ['click-select'], }, // 解绑目前图模式的所有事件监听; // 生成新的 Behavior ,进行事件初始化; // 绑定新的行为对应的事件监听。 graph.setMode('edit'); graph.addBehaviors graph.removeBehaviors |
状态State
判断是否该使用 state 的原则很简单,从交互和业务两个层面来看:
某个交互动作要改变节点或边的样式及属性;
呈现给用户的内容会根据数据改变(如 1 代表成功,0 代表失败)。
满足上述条件其一,则应该使用 state。
在 G6 中,有两种方式配置不同状态的样式:
在实例化 Graph 时,通过 nodeStateStyles
和 edgeStateStyles
对象定义;
在节点/边数据中,在 stateStyles
对象中定义状态;
设置状态:setItemState
取消状态:clearItemStates
更新状态:updateItem
graph.setItemState(item, stateName, stateValue) graph.clearItemStates(item, 'selected'); // 实例化 nodeStateStyles: { }, //数据 stateStyles: { }, //updataItem stateStyles: { // 修改 hover 状态下的样式 hover: { opacity: 0.1, // 修改 name 为 'node-label' 的子图形 hover 状态下的样式 'node-text': { stroke: 'blue', }, }, }, //优先级 item.hasState('active'); |
Grid
Minimap
ImageMinimap
Edge Bundling
Menu
ToolBar
TimeBar
Tooltip
Fisheye
EdgeFilterLens
// 实例化 Image Minimap 插件 const imageMinimap = new G6.ImageMinimap({ width: 200, graphImg: 'https://gw.alipayobjects.com/mdn/rms_f8c6a0/afts/img/A*eD7nT6tmYgAAAAAAAAAAAABkARQnAQ' }); const graph = new G6.Graph({ //... 其他配置项 plugins: [imageMinimap], // 配置 imageMinimap 插件 }); |
计算nodes,edges x/y
全局
//全局 animate: true, // Boolean,切换布局时是否使用动画过度,默认为 false graph.updateLayout(cfg) 布局的变化 graph.changeData() 数据的变化 |
元素
节点上图形的动画
增加带有动画的背景图形
节点上部分图形的旋转动画
圆点在沿着线运动
虚线运动的效果
线从无到有的效果
//开始 shape.animate( (ratio) => { // 每一帧的操作,入参 ratio:这一帧的比例值(Number)。返回值:这一帧需要变化的参数集(Object)。 // 先变大、再变小 const diff = ratio <= 0.5 ? ratio * 10 : (1 - ratio) * 10; let radius = cfg.size; if (isNaN(radius)) radius = radius[0]; // 返回这一帧需要变化的参数集,这里只包含了半径 return { r: radius / 2 + diff, }; }, { // 动画重复 repeat: true, duration: 3000, easing: 'easeCubic', }, ); // 一次动画持续的时长为 3000,动画效果为 'easeCubic' //结束 shape.stopAnimate(); |