常用配置
本文使用到的配置以及后续 Tutorial 将会使用到到常用配置如下:
配置项 | 类型 | 选项 / 示例 | 默认 | 说明 |
---|---|---|---|---|
fitView | Boolean | true / false | false | 是否将图适配到画布大小,可以防止超出画布或留白太多。 |
fitViewPadding | Number / Array | 20 / [ 20, 40, 50, 20 ] | 0 | 画布上的四周留白宽度。 |
animate | Boolean | true / false | false | 是否启用图的动画。 |
modes | Object | { default: [ 'drag-node', 'drag-canvas' ] } | null | 图上行为模式的集合。由于比较复杂,按需参见:G6 中的 Mode 教程。 |
defaultNode | Object | { type: 'circle', color: '#000', style: { ...... } } | null | 节点默认的属性,包括节点的一般属性和样式属性(style)。 |
defaultEdge | Object | { type: 'polyline', color: '#000', style: { ...... } } | null | 边默认的属性,包括边的一般属性和样式属性(style)。 |
nodeStateStyles | Object | { hover: { ...... }, select: { ...... } } | null | 节点在除默认状态外,其他状态下的样式属性(style)。例如鼠标放置(hover)、选中(select)等状态。 |
edgeStateStyles | Object | { hover: { ...... }, select: { ...... } } | null | 边在除默认状态外,其他状态下的样式属性(style)。例如鼠标放置(hover)、选中(select)等状态。 |
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>Tutorial Demo</title>
</head>
<body>
<div id="mountNode"></div>
<script src="https://gw.alipayobjects.com/os/antv/pkg/_antv.g6-3.7.1/dist/g6.min.js"></script>
<!-- 4.x and later versions -->
<!-- <script src="https://gw.alipayobjects.com/os/lib/antv/g6/4.3.11/dist/g6.min.js"></script> -->
<script>
const graph = new G6.Graph({
container: 'mountNode',
width: 1000,
height: 600,
fitView: true,
fitViewPadding: [20, 40, 50, 20],
});
const main = async () => {
const response = await fetch(
'https://gw.alipayobjects.com/os/basement_prod/6cae02ab-4c29-44b2-b1fd-4005688febcb.json',
);
const remoteData = await response.json();
graph.data(remoteData);
graph.render();
};
main();
</script>
</body>
</html>
元素及其配置
图的元素特指图上的节点 Node
和边 Edge
。在上节中,我们已经将 Tutorial 案例的图绘制了出来,但是各个元素及其 label
在视觉上很简陋。本文通过将上一章节中简陋的元素美化成如下效果,介绍元素的属性、配置方法。
图的元素
图的元素特指图上的节点 Node
和边 Edge
。G6 内置了一系列 内置的节点 和 内置的边,供用户自由选择。G6 不同的内置节点或不同的内置边主要区别在于元素的 图形 Shape,例如,节点可以是圆形、矩形、图片等。
元素的属性
不论是节点还是边,它们的属性分为两种:
- 样式属性
style
:对应 Canvas 中的各种样式,在元素状态 State 发生变化时,可以被改变; - 其他属性:例如图形类型(
type
)、id(id
)一类在元素状态 State 发生变化时不能被改变的属性。
例如,G6 设定 hover 或 click 节点,造成节点状态的改变,只能自动改变节点的样式属性(如 fill
、stroke
等**)**,其他属性(如 type
等)不能被改变。如果需要改变其他属性,要通过 graph.updateItem 手动配置。样式属性是一个名为 style
的对象, style
字段与其他属性并行。
数据结构
以节点元素为例,其属性的数据结构如下:
{
id: 'node0', // 元素的 id
type: 'circle', // 元素的图形
size: 40, // 元素的大小
label: 'node0' // 标签文字
visible: true, // 控制初次渲染显示与隐藏,若为 false,代表隐藏。默认不隐藏
labelCfg: { // 标签配置属性
positions: 'center',// 标签的属性,标签在元素中的位置
style: { // 包裹标签样式属性的字段 style 与标签其他属性在数据结构上并行
fontSize: 12 // 标签的样式属性,文字字体大小
// ... // 标签的其他样式属性
}
}
// ..., // 其他属性
style: { // 包裹样式属性的字段 style 与其他属性在数据结构上并行
fill: '#000', // 样式属性,元素的填充色
stroke: '#888', // 样式属性,元素的描边色
// ... // 其他样式属性
}
}
边元素的属性数据结构与节点元素相似,只是其他属性中多了 source
和 target
字段,代表起始和终止节点的 id
。 细化在图 1 中 Tutorial 案例 的视觉需求,我们需要完成:
- 视觉效果:
- R1: 节点的描边和填充色,对应节点样式属性:
fill
,stroke
; - R2: 节点上标签文本的颜色,对应节点其他属性:
labelCfg
; - R3: 边的透明度和颜色,对应边样式属性:
opacity
,stroke
; - R4: 边上标签文本的方向和颜色,对应边其他属性:
labelCfg
;
- R1: 节点的描边和填充色,对应节点样式属性:
- 数据与视觉映射:
- R5: 根据数据中节点的
class
属性映射节点的形状,对应节点其他属性:type
; - R6: 根据数据中边的
weight
属性映射边的粗细,对应边样式属性:lineWidth
。
- R5: 根据数据中节点的
数据格式
{
"nodes": [
{"id": "0", "label": "n0", "class": "c0","x": 1000, "y": -100 },
{"id": "1", "label": "n1", "class": "c0","x": 300, "y": -10 },
{"id": "2", "label": "n2", "class": "c0","x": 10, "y": 10 },
{"id": "3", "label": "n3", "class": "c0","x": 320, "y": -100 },
{"id": "4", "label": "n4", "class": "c0","x": 100, "y": 900 },
{"id": "5", "label": "n5", "class": "c0","x": 120, "y": 213 },
{"id": "6", "label": "n6", "class": "c1","x": 543, "y": 12 },
{"id": "7", "label": "n7", "class": "c1","x": 543, "y": -100 },
{"id": "8", "label": "n8", "class": "c1","x": 1, "y": 0 },
{"id": "9", "label": "n9", "class": "c1","x": 0, "y": -222 },
{"id": "10", "label": "n10", "class": "c1","x": 435, "y": 69 },
{"id": "11", "label": "n11", "class": "c1","x": 23, "y": 10 },
{"id": "12", "label": "n12", "class": "c1","x": -129, "y": 39 },
{"id": "13", "label": "n13", "class": "c2","x": 234, "y": 843 },
{"id": "14", "label": "n14", "class": "c2","x": -301, "y": 129 },
{"id": "15", "label": "n15", "class": "c2","x": -20, "y": -76 },
{"id": "16", "label": "n16", "class": "c2","x": 1220, "y": -34 },
{"id": "17", "label": "n17", "class": "c2","x": -10, "y": 954 },
{"id": "18", "label": "n18", "class": "c2","x": 492, "y": 123 },
{"id": "19", "label": "n19", "class": "c2","x": 123, "y": -241 }
],
"edges": [
{"source": "0", "target": "1", "label": "e0-1", "weight": 1 },
{"source": "0", "target": "2", "label": "e0-2", "weight": 2 },
{"source": "0", "target": "3", "label": "e0-3", "weight": 3 },
{"source": "0", "target": "4", "label": "e0-4", "weight": 1.4 },
{"source": "0", "target": "5", "label": "e0-5", "weight": 2 },
{"source": "0", "target": "7", "label": "e0-7", "weight": 2 },
{"source": "0", "target": "8", "label": "e0-8", "weight": 2 },
{"source": "0", "target": "9", "label": "e0-9", "weight": 1.3 },
{"source": "0", "target": "10", "label": "e0-10", "weight": 1.5 },
{"source": "0", "target": "11", "label": "e0-11", "weight": 1 },
{"source": "0", "target": "13", "label": "e0-13", "weight": 10 },
{"source": "0", "target": "14", "label": "e0-14", "weight": 2 },
{"source": "0", "target": "15", "label": "e0-15", "weight": 0.5 },
{"source": "0", "target": "16", "label": "e0-16", "weight": 0.8 },
{"source": "2", "target": "3", "label": "e2-3", "weight": 1 },
{"source": "4", "target": "5", "label": "e4-5", "weight": 1.4 },
{"source": "4", "target": "6", "label": "e4-6", "weight": 2.1 },
{"source": "5", "target": "6", "label": "e5-6", "weight": 1.9 },
{"source": "7", "target": "13", "label": "e7-13", "weight": 0.5 },
{"source": "8", "target": "14", "label": "e8-14", "weight": 0.8 },
{"source": "9", "target": "10", "label": "e9-10", "weight": 0.2 },
{"source": "10", "target": "14", "label": "e10-14", "weight": 1 },
{"source": "10", "target": "12", "label": "e10-12", "weight": 1.2 },
{"source": "11", "target": "14", "label": "e11-14", "weight": 1.2 },
{"source": "12", "target": "13", "label": "e12-13", "weight": 2.1 },
{"source": "16", "target": "17", "label": "e16-17", "weight": 2.5 },
{"source": "16", "target": "18", "label": "e16-18", "weight": 3 },
{"source": "17", "target": "18", "label": "e17-18", "weight": 2.6 },
{"source": "18", "target": "19", "label": "e18-19", "weight": 1.6 }
]
}
配置属性
在 G6 中,根据不同的场景需求,有 7 种配置元素属性的方式。这里,我们简单介绍其中的两种:
- 实例化图时配置元素的全局属性;
- 在数据中配置。
1. 实例化图时全局配置
**适用场景:**所有节点统一的属性配置,所有边统一的属性配置。 **使用方式:**使用图的两个配置项:
defaultNode
:节点在默认状态下的样式属性(style
)和其他属性;defaultEdge
:边在默认状态下的样式属性(style
)和其他属性。
注意: 由于是统一的配置,不能根据数据中的属性(如 class
、weight
)等值的不同进行个性化设置,因此只能满足 R1、R2、R3、R4 需求。
通过如下方式在实例化图时 defaultNode
和 defaultEdge
,可以完成上图效果:
const graph = new G6.Graph({
// ... // 图的其他配置
// 节点在默认状态下的样式配置(style)和其他配置
defaultNode: {
size: 30, // 节点大小
// ... // 节点的其他配置
// 节点样式配置
style: {
fill: 'steelblue', // 节点填充色
stroke: '#666', // 节点描边色
lineWidth: 1, // 节点描边粗细
},
// 节点上的标签文本配置
labelCfg: {
// 节点上的标签文本样式配置
style: {
fill: '#fff', // 节点标签文字颜色
},
},
},
// 边在默认状态下的样式配置(style)和其他配置
defaultEdge: {
// ... // 边的其他配置
// 边样式配置
style: {
opacity: 0.6, // 边透明度
stroke: 'grey', // 边描边颜色
},
// 边上的标签文本配置
labelCfg: {
autoRotate: true, // 边上的标签文本根据边的方向旋转
},
},
});
2. 在数据中配置
**适用场景:**不同节点/边可以有不同的个性化配置。 因此,这种配置方式可以满足 R5、R6 需求。 **使用方式:**可以直接将配置写入数据文件;也可以在读入数据后,通过遍历的方式写入配置。这里展示读入数据后,通过遍历的方式写入配置。下面代码展示了如何遍历数据进行属性的配置:
const nodes = remoteData.nodes;
nodes.forEach((node) => {
if (!node.style) {
node.style = {};
}
switch (
node.class // 根据节点数据中的 class 属性配置图形
) {
case 'c0': {
node.type = 'circle'; // class = 'c0' 时节点图形为 circle
break;
}
case 'c1': {
node.type = 'rect'; // class = 'c1' 时节点图形为 rect
node.size = [35, 20]; // class = 'c1' 时节点大小
break;
}
case 'c2': {
node.type = 'ellipse'; // class = 'c2' 时节点图形为 ellipse
node.size = [35, 20]; // class = 'c2' 时节点大小
break;
}
}
});
graph.data(remoteData);
- 可以看到,图中有一些节点被渲染成了矩形,还有一些被渲染成了椭圆形。除了设置
type
属性之外,我们还覆盖了上文全局配置的节点的size
属性,在矩形和椭圆的情况下,size
是一个数组;而在默认圆形的情况下,G6 将仍然读取全局配置的size
属性为数字30
。 - 也就是说,动态配置属性让我们既可以根据数据的不同配置不同的属性值,也可以有能力覆盖全局静态的属性值。
进一步地,我们尝试根据数据的比重不同,配置不一样边的粗细:
// 遍历边数据
const edges = remoteData.edges;
edges.forEach((edge) => {
if (!edge.style) {
edge.style = {};
}
edge.style.lineWidth = edge.weight; // 边的粗细映射边数据中的 weight 属性数值
});
graph.data(remoteData);
边的粗细已经按照数据的比重成功渲染了出来,但是边原有的样式(透明度、颜色)却丢失了。这是因为 动态配置属性会覆盖全局配置属性,这里配置了 style.lineWidth
,导致覆盖了全局的 style
对象。解决办法是将被覆盖的边的样式都移到动态配置里面来:
- 完整代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>Tutorial Demo</title>
</head>
<body>
<div id="mountNode"></div>
<script src="https://gw.alipayobjects.com/os/antv/pkg/_antv.g6-3.7.1/dist/g6.min.js"></script>
<!-- 4.x and later versions -->
<!-- <script src="https://gw.alipayobjects.com/os/lib/antv/g6/4.3.11/dist/g6.min.js"></script> -->
<script>
const graph = new G6.Graph({
container: 'mountNode',
width: 800,
height: 600,
fitView: true,
fitViewPadding: [20, 40, 50, 20],
defaultNode: {
size: 30,
labelCfg: {
style: {
fill: '#fff',
},
},
},
defaultEdge: {
labelCfg: {
autoRotate: true,
},
},
});
const main = async () => {
const response = await fetch(
'https://gw.alipayobjects.com/os/basement_prod/6cae02ab-4c29-44b2-b1fd-4005688febcb.json',
);
const remoteData = await response.json();
const nodes = remoteData.nodes;
const edges = remoteData.edges;
nodes.forEach((node) => {
if (!node.style) {
node.style = {};
}
node.style.lineWidth = 1;
node.style.stroke = '#666';
node.style.fill = 'steelblue';
switch (node.class) {
case 'c0': {
node.type = 'circle';
break;
}
case 'c1': {
node.type = 'rect';
node.size = [35, 20];
break;
}
case 'c2': {
node.type = 'ellipse';
node.size = [35, 20];
break;
}
}
});
edges.forEach((edge) => {
if (!edge.style) {
edge.style = {};
}
edge.style.lineWidth = edge.weight;
edge.style.opacity = 0.6;
edge.style.stroke = 'grey';
});
graph.data(remoteData);
graph.render();
};
main();
</script>
</body>
</html>
图布局 Layout
- 当数据中没有节点位置信息,或者数据中的位置信息不满足需求时,需要借助一些布局算法对图进行布局。
般图:
Random Layout:随机布局;
Force Layout:经典力导向布局:
力导向布局:一个布局网络中,粒子与粒子之间具有引力和斥力,从初始的随机无序的布局不断演变,逐渐趋于平衡稳定的布局方式称之为力导向布局。适用于描述事物间关系,比如人物关系、计算机网络关系等。
Circular Layout:环形布局;
Radial Layout:辐射状布局;
MDS Layout:高维数据降维算法布局;
Fruchterman Layout:Fruchterman 布局,一种力导布局;
Dagre Layout:层次布局;
Concentric Layout:同心圆布局,将重要(默认以度数为度量)的节点放置在布局中心;
Grid Layout:格子布局,将节点有序(默认是数据顺序)排列在格子上。
树图布局:
- Dendrogram Layout:树状布局(叶子节点布局对齐到同一层);
- CompactBox Layout:紧凑树布局;
- Mindmap Layout:脑图布局;
- Indented Layout:缩进布局。
各种布局方法的具体介绍及其配置参见 图布局 API 或 树图布局 API。
取消自动适配画布
我们在之前的教程里面,为了能够将超出画布的图适配到视野中,在实例化图时使用了 fitView
配置项。这节开始我们将会去掉这个特性。因为复杂的布局系统会打破适配的规则,反而会造成更多的困扰。让我们将相关的适配代码变为注释:
const graph = new G6.Graph({
// ...
// fitView: true,
// fitViewPadding: [ 20, 40, 50, 20 ]
});
默认布局
当实例化图时没有配置布局时:
- 若数据中节点有位置信息(
x
和y
),则按照数据的位置信息进行绘制; - 若数据中节点没有位置信息,则默认使用 Random Layout 进行布局。
配置布局
G6 使用布局的方式非常简单,在图实例化的时候,加上 layout 配置即可。下面代码在实例化图时设置了布局方法为 type: 'force'
,即经典力导向图布局。并设置了参数 preventOverlap: true
,表示希望节点不重叠。力导向布局的更多配置项参见:Layout API。
const graph = new G6.Graph({
// ... // 其他配置项
layout: {
// Object,可选,布局的方法及其配置项,默认为 random 布局。
type: 'force', // 指定为力导向布局
preventOverlap: true, // 防止节点重叠
// nodeSize: 30 // 节点大小,用于算法中防止节点重叠时的碰撞检测。由于已经在上一节的元素配置中设置了每个节点的 size 属性,则不需要在此设置 nodeSize。
},
});
图的交互 Behavior
G6 封装了一系列交互方法,方便用户直接使用。本文将为 Tutorial 案例 增加简单的交互:hover 节点、点击节点、点击边、放缩画布、拖拽画布。
交互行为 Behavior
G6 中的交互行为。G6 内置了一系列交互行为,用户可以直接使用。简单地理解,就是可以一键开启这些交互行为:
drag-canvas
:拖拽画布;zoom-canvas
:缩放画布。
更多详见:交互行为 Behavior
交互管理 Mode
Mode 是 G6 交互行为的管理机制,一个 mode 是多种行为 Behavior 的组合,允许用户通过切换不同的模式进行交互行为的管理。由于该概念较为复杂,在本入门教程中,读者不需要对该机制深入理解。如有需求,参见 交互模式 Mode。
交互状态 State
状态 State 是 G6 中的状态机制。用户可以为图中的元素(节点/边)设置不同的状态及不同状态下的样式。在状态发生变化时,G6 自动更新元素的样式。例如,可以为节点设置状态 'click'
为 true
或 false
,并为节点设置 'click'
的样式为加粗节点边框。当 'click'
状态被切换为 true
时,节点的边框将会被加粗,'click'
状态被切换为 false
时,节点的样式恢复到默认。在下面的使用方法中,将会有具体例子。
使用方法
拖拽、缩放——内置的交互行为
- 在 G6 中使用内置 Behavior 的方式非常简单,只需要在图实例化时配置
modes
。拖拽和缩放属于 G6 内置交互行为,修改代码如下:
const graph = new G6.Graph({
// ... // 其他配置项
modes: {
default: ['drag-canvas', 'zoom-canvas', 'drag-node'], // 允许拖拽画布、放缩画布、拖拽节点
},
});
除了直接使用内置交互名称外,也可以为 Behavior 配置参数,例如放缩画布的敏感度、最大/最小放缩程度等,具体用法参见 交互行为 Behavior。
上面代码中的 modes
定义了 G6 的模式,default
是默认的模式,还可以允许有其他的模式,比如:编辑模式 edit
等。不同的模式,用户能进行的行为可以不同,比如默认模式能拖拽画布,编辑模式不允许拖拽画布:
Hover、Click 改变样式——状态式交互
有时我们希望通过交互可以将元素样式变成特定样式, 鼠标 hover 节点、点击节点、点击边时,样式发生了变化。这里涉及到了 G6 中 状态 State 的概念。简单地说,是否 hover
、click
、任何操作(可以是自己起的状态名),都可以称为一种状态(state)。用户可以自由设置不同状态下的元素样式。要达到交互更改元素样式,需要两步:
- Step 1: 设置各状态下的元素样式;
- Step 2: 监听事件并切换元素状态。
设置各状态下的元素样式
在实例化图时,通过 nodeStateStyles
和 edgeStateStyles
两个配置项可以配置元素在不同状态下的样式。 为达到 Tutorial 案例 中的效果:
- 鼠标 hover 节点时,该节点颜色变浅;
- 点击节点时,该节点边框加粗变黑;
- 点击边时,该边变成蓝色。
下面代码设置了节点分别在 hover
和 click
状态为 true
时的样式,边在 click
状态为 true
时的样式:
const graph = new G6.Graph({
// ... // 其他配置项
// 节点不同状态下的样式集合
nodeStateStyles: {
// 鼠标 hover 上节点,即 hover 状态为 true 时的样式
hover: {
fill: 'lightsteelblue',
},
// 鼠标点击节点,即 click 状态为 true 时的样式
click: {
stroke: '#000',
lineWidth: 3,
},
},
// 边不同状态下的样式集合
edgeStateStyles: {
// 鼠标点击边,即 click 状态为 true 时的样式
click: {
stroke: 'steelblue',
},
},
});
监听事件并切换元素状态
G6 中所有元素监听都挂载在图实例上,如下代码中的 graph
对象是 G6.Graph 的实例,graph.on()
函数监听了某元素类型(node
/ edge
)的某种事件(click
/ mouseenter
/ mouseleave
/ ... 所有事件参见:Event API)。
// 在图实例 graph 上监听
graph.on('元素类型:事件名', (e) => {
// do something
});
现在,我们通过下面代码,为 Tutorial 案例 增加点和边上的监听事件,并在监听函数里使用 graph.setItemState()
改变元素的状态:
// 鼠标进入节点
graph.on('node:mouseenter', (e) => {
const nodeItem = e.item; // 获取鼠标进入的节点元素对象
graph.setItemState(nodeItem, 'hover', true); // 设置当前节点的 hover 状态为 true
});
// 鼠标离开节点
graph.on('node:mouseleave', (e) => {
const nodeItem = e.item; // 获取鼠标离开的节点元素对象
graph.setItemState(nodeItem, 'hover', false); // 设置当前节点的 hover 状态为 false
});
// 点击节点
graph.on('node:click', (e) => {
// 先将所有当前是 click 状态的节点置为非 click 状态
const clickNodes = graph.findAllByState('node', 'click');
clickNodes.forEach((cn) => {
graph.setItemState(cn, 'click', false);
});
const nodeItem = e.item; // 获取被点击的节点元素对象
graph.setItemState(nodeItem, 'click', true); // 设置当前节点的 click 状态为 true
});
// 点击边
graph.on('edge:click', (e) => {
// 先将所有当前是 click 状态的边置为非 click 状态
const clickEdges = graph.findAllByState('edge', 'click');
clickEdges.forEach((ce) => {
graph.setItemState(ce, 'click', false);
});
const edgeItem = e.item; // 获取被点击的边元素对象
graph.setItemState(edgeItem, 'click', true); // 设置当前边的 click 状态为 true
});
插件与工具
插件
使用插件时,有三个步骤: Step 1: 引入插件; Step 2: 实例化插件; Step 3: 在实例化图时将插件的实例配置到图上。
Minimap 是 G6 的插件之一,引入 G6 后可以直接使用。实例化 Minimap 对象,并将其配置到图实例的插件列表里即可:
// 实例化 minimap 插件
const minimap = new G6.Minimap({
size: [100, 100],
className: 'minimap',
type: 'delegate',
});
// 实例化图
const graph = new G6.Graph({
// ... // 其他配置项
plugins: [minimap], // 将 minimap 实例配置到图上
});
Image Minimap
- 由于 Minimap 的原理是将主画布内容复制到 minimap 的画布上,在大数据量下可能会造成双倍的绘制效率成本。
- 为缓解该问题,Image Minimap 采用另一种机制,根据提供的图片地址或 base64 字符串
graphImg
绘制<img />
代替 minimap 上的 canvas。 - 该方法可以大大减轻两倍 canvas 绘制的压力。
- 但
graphImg
完全交由 G6 的用户控制,需要注意主画布更新时需要使用updateGraphImg
方法替换graphImg
。
使用方法
实例化 Image Minimap 插件时,graphImg
是必要参数。建议在主画布更新时使用 updateGraphImg(img)
同步更新 minimap 图片,其中参数 img
是图片地址或 base64 文本。
// 实例化 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 插件
});
graph.data(data);
graph.render()
... // 一些主画布更新操作
imageMinimap.updateGraphImg(img); // 使用新的图片(用户自己生成)替换 minimap 图片
Grid 网格
网格可用于辅助用户在拖拽节点时对齐到网格。
// const minimap = ...
// 实例化 grid 插件
const grid = new G6.Grid();
// 实例化图
const graph = new G6.Graph({
// ... // 其他配置项
plugins: [minimap, grid], // 将 grid 实例配置到图上
});
交互工具
交互工具是指配置到图上交互模式中的工具。使用交互工具时,有两个步骤: Step 1: 在实例化图时配置 modes
; Step 2: 为交互工具定义样式。
tooltip 节点提示框
节点提示框可以用在节点的详细信息的展示。当鼠标滑过节点时,显示一个浮层告知节点的详细信息。更多配置参见 内置交互 tooltip。
- 实例化图时配置
'tooltip'
到modes
中:
const graph = new G6.Graph({
modes: {
default: [
// ...
{
type: 'tooltip', // 提示框
formatText(model) {
// 提示框文本内容
const text = 'label: ' + model.label + '<br/> class: ' + model.class;
return text;
},
},
],
},
});
- 由于 tooltip 实际上是一个悬浮的
<div>
标签,因此可在 HTML 的<style>
标签或 CSS 中设置样式。下面展示在 HTML 中设置样式:
<head>
<meta charset="UTF-8" />
<title>Tutorial Demo</title>
<style>
/* 提示框的样式 */
.g6-tooltip {
border: 1px solid #e2e2e2;
border-radius: 4px;
font-size: 12px;
color: #545454;
background-color: rgba(255, 255, 255, 0.9);
padding: 10px 8px;
box-shadow: rgb(174, 174, 174) 0px 0px 10px;
}
</style>
</head>
edge-tooltip 边提示框
边提示框可以用在边的详细信息的展示。当鼠标滑过边时,显示一个浮层告知边的详细信息。更多配置参见 内置交互 edge-tooltip。
const graph = new G6.Graph({
modes: {
default: [
// ...
{
type: 'tooltip', // 节点提示框
// ...
},
{
type: 'edge-tooltip', // 边提示框
formatText(model) {
// 边提示框文本内容
const text =
'source: ' +
model.source +
'<br/> target: ' +
model.target +
'<br/> weight: ' +
model.weight;
return text;
},
},
],
},
});
与 tooltip 相同,edge-tooltip 是一个悬浮的 <div>
标签,可以使用与 tooltip 相同的方法设置其悬浮框的样式。
G6 的动画分为两个层次:
- 图全局动画:图整体变化时的动画过渡;
- 元素动画:节点和边的动画效果。
全局动画
G6 的全局动画指通过图实例进行操作时,产生的动画效果。例如:
graph.updateLayout(cfg)
通过实例化图时配置 animate: true
,可以达到每次进行上述操作时,动画效果变化的目的。
例子
const graph = new G6.Graph({
// ... // 其他配置项
animate: true, // Boolean,可选,全局变化时否使用动画过渡
});
G6 核心概念总览
图 Graph
- 初始化和渲染
图形(Shape)(选读)
- 图形和属性
- 关键图形(Key Shape)
- 图形分组(Shape group)
- 图形变换 (Transform)
图元素(节点、边、Combo)
- 节点
- 内置节点
- 自定义节点
- 节点的连接点 anchorPoints
- 边
- 内置边
- 自定义边
- Combo
- 内置 Combo
- 自定义 Combo
- Combo 机制
- 创建与拆分 Combo
- 高级样式
- 设置元素背景
- 设置元素渐变色
- 设置纹理
- 更新文本样式
- 高级操作
- 更新节点或边的样式
- 层级调整
- 显示隐藏
- 锁定/解锁
图布局
- 内置布局
- 自定义布局
交互与事件
- 监听与绑定事件
- 内置交互行为
- 自定义交互行为(选读)
- 交互模式 Mode
动画
- 全局动画
- 元素动画
图算法
插件
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>Tutorial Demo</title>
</head>
<style>
/* 提示框的样式 */
.g6-tooltip {
border: 1px solid #e2e2e2;
border-radius: 4px;
font-size: 12px;
color: #545454;
background-color: rgba(255, 255, 255, 0.9);
padding: 10px 8px;
box-shadow: rgb(174, 174, 174) 0px 0px 10px;
}
</style>
<body>
<!-- 引入 G6 -->
<script src="https://gw.alipayobjects.com/os/antv/pkg/_antv.g6-3.7.1/dist/g6.min.js"></script>
<!-- 4.x and later versions -->
<!-- <script src="https://gw.alipayobjects.com/os/lib/antv/g6/4.3.11/dist/g6.min.js"></script> -->
<div id="mountNode"></div>
<script>
console.log(G6.Global.version);
const initData = {
// 点集
nodes: [
{
id: 'node1', // 节点的唯一标识
x: 100, // 节点横坐标
y: 200, // 节点纵坐标
label: '起始点', // 节点文本
},
{
id: 'node2',
x: 300,
y: 200,
label: '目标点',
},
],
// 边集
edges: [
// 表示一条从 node1 节点连接到 node2 节点的边
{
source: 'node1', // 起始点 id
target: 'node2', // 目标点 id
label: '我是连线', // 边的文本
},
],
};
// 实例化 grid 插件
const grid = new G6.Grid();
const graph = new G6.Graph({
container: 'mountNode', // 指定挂载容器
width: 800, // 图的宽度
height: 500, // 图的高度
plugins: [ grid], // 将 grid 实例配置到图上
// fitView: true,
// fitViewPadding: [20, 40, 50, 20],
modes: {
default: ['drag-canvas', 'zoom-canvas', 'drag-node'
, {
type: 'tooltip', // 提示框
formatText(model) {
// 提示框文本内容
const text = 'label: ' + model.label + '<br/> class: ' + model.class;
return text;
},
},
], // 允许拖拽画布、放缩画布、拖拽节点
},
layout: {
// Object,可选,布局的方法及其配置项,默认为 random 布局。
type: 'force', // 指定为力导向布局
preventOverlap: true, // 防止节点重叠
// nodeSize: 30 // 节点大小,用于算法中防止节点重叠时的碰撞检测。由于已经在上一节的元素配置中设置了每个节点的 size 属性,则不需要在此设置 nodeSize。
linkDistance: 300, // 指定边距离为100
},
nodeStateStyles: {
// 鼠标 hover 上节点,即 hover 状态为 true 时的样式
hover: {
fill: 'lightsteelblue',
},
// 鼠标点击节点,即 click 状态为 true 时的样式
click: {
stroke: '#000',
lineWidth: 3,
},
},
// 边不同状态下的样式集合
edgeStateStyles: {
// 鼠标点击边,即 click 状态为 true 时的样式
click: {
stroke: 'steelblue',
},
},
// 节点在默认状态下的样式配置(style)和其他配置
defaultNode: {
size: 30, // 节点大小
// ... // 节点的其他配置
// 节点样式配置
style: {
fill: 'steelblue', // 节点填充色
stroke: '#666', // 节点描边色
lineWidth: 1, // 节点描边粗细
},
// 节点上的标签文本配置
labelCfg: {
// 节点上的标签文本样式配置
style: {
fill: '#fff', // 节点标签文字颜色
},
},
},
});
// graph.data(initData); // 加载数据
// graph.render(); // 渲染
// fetch 函数允许我们发起网络请求,加载数据,而其异步的过程可以通过 async/await 进行更合理的控制。这里我们为了方便起见,将主要逻辑放在了 main 函数里面。
// const graph = ...
const main = async () => {
const response = await fetch(
'https://gw.alipayobjects.com/os/basement_prod/6cae02ab-4c29-44b2-b1fd-4005688febcb.json',
);
const remoteData = await response.json();
// 在读入数据后,通过遍历的方式写入配置。
remoteData.nodes.forEach((node) => {
if (!node.style) {
node.style = {};
}
switch (
node.class // 根据节点数据中的 class 属性配置图形
) {
case 'c0': {
node.type = 'circle'; // class = 'c0' 时节点图形为 circle
break;
}
case 'c1': {
node.type = 'rect'; // class = 'c1' 时节点图形为 rect
node.size = [35, 20]; // class = 'c1' 时节点大小
break;
}
case 'c2': {
node.type = 'ellipse'; // class = 'c2' 时节点图形为 ellipse
node.size = [35, 20]; // class = 'c2' 时节点大小
break;
}
}
});
// ... 很多节点的 x 和 y 不在图的宽高(width: 800, height: 600)范围内:
const edges = remoteData.edges;
edges.forEach((edge) => {
if (!edge.style) {
edge.style = {};
}
edge.style.lineWidth = edge.weight; // 边的粗细映射边数据中的 weight 属性数值
});
graph.data(remoteData); // 加载远程数据
graph.render(); // 渲染
};
main();
// 鼠标进入节点
graph.on('node:mouseenter', (e) => {
console.log(e);
const nodeItem = e.item; // 获取鼠标进入的节点元素对象
graph.setItemState(nodeItem, 'hover', true); // 设置当前节点的 hover 状态为 true
});
// 鼠标离开节点
graph.on('node:mouseleave', (e) => {
const nodeItem = e.item; // 获取鼠标离开的节点元素对象
graph.setItemState(nodeItem, 'hover', false); // 设置当前节点的 hover 状态为 false
});
/*
G6 提供了图的两个相关配置项:
fitView:设置是否将图适配到画布中;
fitViewPadding:画布上四周的留白宽度。
我们将实例化图的代码更改为如下形式:
const graph = new G6.Graph({
// ...
fitView: true,
fitViewPadding: [20, 40, 50, 20],
});
*/
</script>
</body>
</html>