图
// 创建 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
常用配置项
使用 canvas 或 svg 渲染
renderer
自适应画布
fitView
fitViewPadding
fitCenter
全局元素配置
defaultNode
defaultEdge
nodeStateStyles
edgeStateStyles
布局相关
layout
交互行为相关
modes
动画相关
animate
animateCfg
插件
plugins
图形
每个图元素由图形(Shape) 组成,且都会有自己的唯一关键图形(keyShape)。
图形 Shape
- circle:圆;
- rect:矩形;
- ellipse:椭圆;
- polygon:多边形;
- fan:扇形;
- image:图片;
- marker:标记;
- path:路径;
- text:文本;
- dom(svg):DOM
各图形 Shape 的通用方法
attr(name)
获取实例的属性值。
attr(name, value)
更新实例的单个绘图属性。
attr({...})
批量更新实例绘图属性。
KeyShape
代表元素的图形
group
- 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] /> ...
value
边
- 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、节点与分组的边、分组与分组的边、空的节点分组等
{
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
自定义交互 Behavior
在交互行为上, G6 主要考虑了三个场景:
- 展示关系数据;
- 可视化建模;
- 图分析。
在这些场景中只要用户可能无法一眼看清楚所有需要的信息,都需要进行交互,例如:
- 图太大,需要缩放;
- 单个节点上展示的信息太少,需要通过 tooltip 显示详情;
- 对节点进行增删改查。
交互模式 Mode
- 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');
插件**
// 实例化 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();