使用布局 Layout

简介

图布局是指图中节点的排布方式,根据图的数据结构不同,布局可以分为两类:一般图布局、树图布局。G6 为这两类图都内置了一些常用的图布局算法。使用内置的图布局可以完成布局的参数、方法、数据的切换等。

除了内置布局方法外,一般图布局还支持 自定义布局 机制。

事实上,G6 的布局是自由的,内置布局算法仅仅是操作了数据中节点的 xy 值。因此,除了使用内置布局以及自定义的一般图布局外,用户还可以使用外部图布局算法,计算节点位置后赋值到数据中节点的 xy 字段上,G6 便可以根据该位置信息进行绘制。

本文将逐一介绍内置的布局算法,及其使用方式。

G6 布局方法总览

一般图 Graph

树图 TreeGraph

一般图 Graph

配置一般图布局

用户可以通过在实例化图时使用图的配置项 layout 指定布局方法。下面代码在实例化图时设置了布局方法为 type: 'force',即经典力导向图布局。并设置了参数 preventOverlap: truenodeSize: 30,表示希望节点不重叠。节点大小 nodeSize 用于算法中判断节点是否重叠,更多配置项见 Graph 各布局的配置项

  1. const graph = new G6.Graph({
  2. ... // 其他配置项
  3. layout: { // Object,可选,布局的方法及其配置项,默认为 random 布局。
  4. type: 'force',
  5. preventOverlap: true,
  6. nodeSize: 30,
  7. ... // 其他配置
  8. }
  9. });

当实例化图时没有配置布局时:

  • 若数据中节点有位置信息(xy),则按照数据的位置信息进行绘制;
  • 若数据中节点没有位置信息,则默认使用 Random Layout 进行布局。

一般图布局方法

图布局通用 API:Layout API

Random

使用布局 Layout - 图1 描述:随机布局。APIRandom API参数

参数名类型示例默认值说明
centerArray[ 0, 0 ]图的中心随机布局的中心
widthNumber300图的宽
heightNumber300图的高

Force

使用布局 Layout - 图2 描述:经典力导向布局。APIForce API参数:与 d3 的力导布局参数相对应。

参数名类型示例默认值说明
centerArray[ 0, 0 ]图的中心随机布局的中心
linkDistanceNumberFunction示例1: 50 示例2:d => { // d 是一条边 if (d.id === 'edge1') { return 100; } return 50;}50
nodeStrengthNumberFunction示例1: -30 示例2:d => { // d 是一个节点 if (d.id === 'node1') { return -100; } return -30;}null
edgeStrengthNumber示例1: 1 示例2:d => { // d 是一个节点 if (d.id === 'node1') { return 10; } return 1;}null边的作用力,默认根据节点的出入度自适应。可以使用回调函数的形式对不同对节点定义不同边作用力(如示例2)
preventOverlapBooleanfalsefalse是否防止重叠,必须配合属性 nodeSize ,只有设置了与当前图节点大小相同的 nodeSize 值,才能够进行节点重叠的碰撞检测。若未设置 nodeSize ,则根据节点数据中的 size 进行碰撞检测。若二者都未设置,则默认以 10 为节点大小进行碰撞检测
nodeSizeArray Number20undefined
nodeSpacing3.1.6 后支持Number / Function示例 1 : 10示例 2 : d => { // d 是一个节点 if (d.id === 'node1') { return 100; } return 10;}0使用布局 Layout - 图3preventOverlaptrue 时生效,防止重叠时节点边缘间距的最小值。可以是回调函数,为不同节点设置不同的最小间距,如示例 2 所示
alphaDecayNumber0.030.028迭代阈值的衰减率。[0, 1],0.028 对应迭代书为 300
alphaMinNumber0.030.001停止迭代的阈值
alphaNumber0.10.3当前阈值
collideStrengthNumber0.81防止重叠的力强度,[0, 1]。
forceSimulationObjectnull自定义 force 方法,若不指定,则使用 d3 的方法。
onTickFunction{}每一次迭代的回调函数
onLayoutEndFunction{}布局完成后的回调函数

Fruchterman

使用布局 Layout - 图4 描述:Fruchterman 布局,一种力导布局。APIFruchterman API参数

参数名类型示例默认值说明
centerArray[ 0, 0 ]图的中心随机布局的中心
maxIterationNumber10001000最大迭代次数
gravityNumber1010重力大小,影响布局的紧凑程度
speedNumber11每次迭代节点移动的速度。速度太快可能会导致强烈震荡
clusteringBooleanfalsefalse是否按照聚类布局
clusterGravityNumber3010聚类内部的重力大小,影响聚类的紧凑程度

Circular

使用布局 Layout - 图5使用布局 Layout - 图6使用布局 Layout - 图7 描述:环形布局。APICircular API参数

参数名类型示例/可选值默认值说明
centerArray[ 0, 0 ]图的中心随机布局的中心
radiusNumber50null圆的半径。若设置了 radius,则 startRadius 与 endRadius 不生效
startRadiusNumber10null螺旋状布局的起始半径
endRadiusNumber100null螺旋状布局的结束半径
clockwiseBooleantruetrue是否顺时针排列
divisionsNumber31节点在环上的分段数(几个段将均匀分布),在 endRadius - startRadius != 0 时生效
orderingStringnull'topology''degree'
angleRatioNumber11从第一个节点到最后节点之间相隔多少个 2*PI

Radial

使用布局 Layout - 图8 描述:辐射状布局。APIRadial API参数

参数名类型示例默认值说明
centerArray[ 0, 0 ]图的中心随机布局的中心
linkDistanceNumber5050边长
maxIterationNumber10001000停止迭代到最大迭代数
focusNodeStringObject'node1'null
unitRadiusNumber10100每一圈距离上一圈的距离。默认填充整个画布,即根据图的大小决定
preventOverlapBooleanfalsefalse是否防止重叠,必须配合属性 nodeSize ,只有设置了与当前图节点大小相同的 nodeSize 值,才能够进行节点重叠的碰撞检测。3.1.6 后支持:若未设置 nodeSize,则将会根据数据中节点的 size 字段数值进行碰撞检测计算。若二者皆未设置,则以节点大小为 10 进行计算。
maxPreventOverlapIterationNumber500200防止重叠步骤的最大迭代次数
nodeSizeNumber1010节点大小(直径)。用于防止节点重叠时的碰撞检测。3.1.6 后支持:若未设置则使用数据中节点的 size 字段数值进行碰撞检测计算。若二者皆未设置,则以节点大小为 10 进行计算。
nodeSpacing3.1.6 后支持NumberFunction示例 1 : 10示例 2 : d => { // d 是一个节点 if (d.id === 'node1') { return 100; } return 10;}0
strictRadialBooleantruefalse是否必须是严格的 radial 布局,即每一层的节点严格布局在一个环上。preventOverlaptrue 时生效。详见 Radial-strictRadial API- 当 preventOverlaptrue,且 strictRadialfalse 时,有重叠的节点严格沿着所在的环展开,但在一个环上若节点过多,可能无法完全避免节点重叠。- 当 preventOverlaptrue,且 strictRadialtrue 时,允许同环上重叠的节点不严格沿着该环布局,可以在该环的前后偏移以避免重叠。

MDS

使用布局 Layout - 图9描述:高维数据降维算法布局。APIMDS API参数

参数名类型示例默认值说明
centerArray[ 0, 0 ]图的中心随机布局的中心
linkDistanceNumber5050边长

Dagre

使用布局 Layout - 图10描述:层次布局。APIDagre API参数

参数名类型示例 / 可选值默认值说明
rankdirString'TB' / 'BT' / 'LR' / 'RL''TB'layout 的方向。T:top;B:bottom;L:left;R:right
alignString'UL' / 'UR' / 'DL' / 'DR''UL'节点对齐方式。U:upper;D:down;L:left;R:right
nodesepNumber4050rankdir 为 'TB' 或 'BT' 时代表节点水平间距(px);在rankdir 为 'LR' 或 'RL' 时代表节点的竖直间距。优先级高于 nodesepFunc
ranksepNumber4050层间距(px)。在rankdir 为 'TB' 或 'BT' 时是竖直方向相邻层间距;在rankdir 为 'LR' 或 'RL' 时代表水平方向相邻层间距。优先级高于 ranksepFunc
nodesepFunc3.1.6 后支持Functiond => { // d 是一个节点 if (d.id === 'node1') { return 100; } return 10;}undefined节点水平间距(px)的回调函数,通过该参数可以对不同节点设置不同的节点间距。在rankdir 为 'TB' 或 'BT' 时是节点的水平间距;在rankdir 为 'LR' 或 'RL' 时是节点的竖直间距。优先级低于 nodesep,即若设置了 nodesep,则 nodesepFunc 不生效
ranksepFunc3.1.6 后支持Functiond => { // d 是一个节点 if (d.id === 'node1') { return 100; } return 10;}undefined层间距(px)的回调函数,通过该参数可以对不同节点设置不同的层间距。在rankdir 为 'TB' 或 'BT' 时是竖直方向相邻层间距;在rankdir 为 'LR' 或 'RL' 时代表水平方向相邻层间距。优先级低于 ranksep,即若设置了 ranksep,则 ranksepFunc 不生效
controlPointsBooleantruetrue是否保留布局连线的控制点

Concentric

使用布局 Layout - 图11注:该算法参考 cytoscape.js,遵守 MIT 开源协议。描述:同心圆布局。APIConcentric API参数

参数名类型示例 / 可选值默认值说明
centerArray[ 0, 0 ]图的中心随机布局的中心
nodeSizeNumber3030节点大小(直径)。用于防止节点重叠时的碰撞检测
minNodeSpacingNumber1010环与环之间最小间距,用于调整半径
preventOverlapBooleanfalsefalse是否防止重叠,必须配合属性 nodeSize ,只有设置了与当前图节点大小相同的 nodeSize 值,才能够进行节点重叠的碰撞检测。若未设置 nodeSize ,则将根据节点数据中的 size 进行碰撞检测。若二者都未设置,则默认以 30 为节点大小进行碰撞检测
sweepNumberMath.PIundefined第一个节点与最后一个节点之间的弧度差
equidistantBooleanfalsefalse环与环之间的距离是否相等
startAngleNumber3.143 / 2 * Math.PI开始放置节点的弧度
clockwiseBooleanfalsefalse是否按照顺时针顺序
maxLevelDiffNumber0.5undefined每一层同心值的求和。若为 undefined,则将会被设置为 maxValue / 4 ,其中 maxValue 为最大的排序依据的属性值。例如,若 sortBy='degree',则 maxValue 为所有节点中度数最大的节点的度数
sortByString'degree''property1''weight'

Grid

使用布局 Layout - 图12注:该算法参考 cytoscape.js,遵守 MIT 开源协议。描述:网格布局。APIGrid API参数

参数名类型示例 / 可选值默认值说明
beginArray[ 0, 0 ][ 0, 0 ]网格开始位置(左上角)
preventOverlapBooleanfalsefalse是否防止重叠,必须配合属性 nodeSize ,只有设置了与当前图节点大小相同的 nodeSize 值,才能够进行节点重叠的碰撞检测。若未设置 nodeSize ,则将根据节点数据中的 size 进行碰撞检测。若二者都未设置,则默认以 30 为节点大小进行碰撞检测
preventOverlapPaddingNumber1010避免重叠时节点的间距 padding。preventOverlap 为 true 时生效
nodeSizeNumber3030节点大小(直径)。用于防止节点重叠时的碰撞检测
condenseBooleanfalsefalse为 false 时表示利用所有可用画布空间,为 true 时表示利用最小的画布空间
rowsNumber5undefined网格的行数,为 undefined 时算法根据节点数量、布局空间、cals(若指定)自动计算
calsNumber5undefined网格的列数,为 undefined 时算法根据节点数量、布局空间、rows(若指定)自动计算
sortByString'degree''property1''weight'

树图 TreeGraph

由于树图特殊性,G6扩展出了 TreeGraph ,详细文档请见:TreeGraph API。树布局是一种能很好展示有一定层次结构数据的布局方式。推荐使用 G6.TreeGraph 实现。

配置树图布局

与一般图 Graph 配置方法相似,通过实例化图时配置 layout 属性设置树的布局,还可以通过 modes 属性为树配置 展开/收缩行为。以下代码声明了一个实例,定义了布局为从左到右结构的基础树图,并且定义了展开收缩行为。

  1. const graph = new G6.TreeGraph({
  2. container: 'mountNode',
  3. modes: {
  4. default: [
  5. {
  6. // 定义展开/收缩行为
  7. type: 'collapse-expand',
  8. },
  9. 'drag-canvas',
  10. ],
  11. },
  12. // 定义布局
  13. layout: {
  14. type: 'dendrogram', // 布局类型
  15. direction: 'LR', // 自左至右布局,可选的有 H / V / LR / RL / TB / BT
  16. nodeSep: 50, // 节点之间间距
  17. rankSep: 100, // 每个层级之间的间距
  18. },
  19. });

树图布局方法

compactBox

描述:紧凑树布局。从根节点开始,同一深度的节点在同一层,并且布局时会将节点大小考虑进去。使用布局 Layout - 图13APICompactBox API参数

参数名类型示例 / 可选值默认值说明
directionString'TB' / 'BT' / 'LR' / 'RL' / 'H' / 'V''LR'layout 的方向。- TB —— 根节点在上,往下布局- BT —— 根节点在下,往上布局使用布局 Layout - 图14 使用布局 Layout - 图15(左)TB。(右)BT。- LR —— 根节点在左,往右布局- RL —— 根节点在右,往左布局使用布局 Layout - 图16 使用布局 Layout - 图17(左)LR。(右)RL。- H —— 根节点在中间,水平对称布局- V —— 根节点在中间,垂直对称布局使用布局 Layout - 图18 使用布局 Layout - 图19> (左)H。(右)V。
getIdFunction(d) => { // d 是一个节点 return d.id;}undefined节点 id 的回调函数
getHeightFunction(d) => { // d 是一个节点 return 10;}undefined节点高度的回调函数
getWidthFunction(d) => { // d 是一个节点 return 20;}undefined节点宽度的回调函数
getVGapFunction(d) => { // d 是一个节点 return 100;}undefined节点纵向间距的回调函数
getHGapFunction(d) => {// d 是一个节点 return 50;}undefined节点横向间距的回调函数
radialBooleantruefalse是否按照辐射状布局。若 radialtrue,建议 direction 设置为 'LR''RL'使用布局 Layout - 图20

dendrogram

描述:生态树布局。不管数据的深度多少,总是叶节点对齐。不考虑节点大小,布局时将节点视为1个像素点。使用布局 Layout - 图21APIDendrogram API参数

参数名类型示例 / 可选值默认值说明
directionString'TB' / 'BT' / 'LR' / 'RL' / 'H' / 'V''LR'layout 的方向。- TB —— 根节点在上,往下布局- BT —— 根节点在下,往上布局使用布局 Layout - 图22使用布局 Layout - 图23> (左)TB。(右)BT。- LR —— 根节点在左,往右布局- RL —— 根节点在右,往左布局使用布局 Layout - 图24使用布局 Layout - 图25> (左)LR。(右)RL。- H —— 根节点在中间,水平对称布局- V —— 根节点在中间,垂直对称布局使用布局 Layout - 图26使用布局 Layout - 图27> (左)H。(右)V。
nodeSepNumber500节点间距
rankSepNumber1000层与层之间的间距
radialBooleantruefalse是否按照辐射状布局。若 radialtrue,建议 direction 设置为 'LR''RL'使用布局 Layout - 图28

indented

描述:缩进树布局。每个元素会占一行/一列。使用布局 Layout - 图29

APIIndented API参数

参数名类型示例 / 可选值默认值说明
directionString'LR' / 'RL' / 'H''LR'layout 的方向。'LR' —— 根节点在左,往右布局(下图左)'RL' —— 根节点在右,往左布局(下图中)'H' —— 根节点在中间,水平对称布局(下图右)indented1indented2indented3
indentNumber8020列间间距
getHeightFunction(d) => { // d 是一个节点 return 10;}undefined节点高度的回调函数
getWidthFunction(d) => { // d 是一个节点 return 20;}undefined节点宽度的回调函数
getSideFunction(d) => { // d 是一个节点 return 'left';}undefined节点放置在根节点左侧或右侧的回调函数,仅对与根节点直接相连的节点有效,设置后将会影响被设置节点的所有子孙节点

mindmap

描述:脑图布局。深度相同的节点将会被放置在同一层,与 compactBox 不同的是,布局不会考虑节点的大小。使用布局 Layout - 图33APIMindmap API参数

参数名类型示例 / 可选值默认值说明
directionString'H' / 'V''H'layout 的方向。- H:horizontal(水平)—— 根节点的子节点分成两部分横向放置在根节点左右两侧使用布局 Layout - 图34- V:vertical (竖直)—— 将根节点的所有孩子纵向排列使用布局 Layout - 图35
getHeightFunction(d) => { // d 是一个节点 return 10;}undefined节点高度的回调函数
getWidthFunction(d) => { // d 是一个节点 return 20;}undefined节点宽度的回调函数
getVGapFunction(d) => { // d 是一个节点 return 100;}18节点纵向间距的回调函数
getHGapFunction(d) => { // d 是一个节点 return 50;}18节点横向间距的回调函数
getSideStringFunction(d) => { // d 是一个节点 return 'left';}'right'

布局的切换机制

G6 提供了两种关于布局的切换机制:

  • updateLayout(params):布局方法或参数的切换
  • changeData():数据的切换

布局方法或参数切换

接口定义:

  1. /**
  2. * 更换布局或布局参数
  3. * @param {String | object} cfg 新布局配置项
  4. * 若 cfg 为 String 或含有 type 字段,且与之前的布局方法不同时将会更换布局
  5. * 否则只是更新原有布局的参数
  6. */
  7. updateLayout(cfg);

布局方法切换:若参数 cfgString 或是含有 type 字段的对象,且与之前的布局方法名不同时将会更换布局。

布局参数切换:若参数 cfg 是对象且其中不含有 type 字段,或指定的布局方法名称与之前的布局方法相同,则保持原有布局方法,仅更新该布局的参数。

数据切换

接口定义:

  1. /**
  2. * 更改源数据,根据新数据重新渲染视图
  3. * @param {object} data 源数据
  4. * @return {object} this
  5. */
  6. changeData(data);

切换示例

期待效果

初始化时使用默认 random 布局,2000 ms 后更换为允许节点重叠的 force 布局,4000 ms 后更换为不允许节点重叠的 force 布局,6000 ms 后更换数据为 data2使用布局 Layout - 图36

完整代码

  1. <!DOCTYPE html>
  2. <html lang="en">
  3. <head>
  4. <meta charset="UTF-8">
  5. <title>Tutorial Layout Demo</title>
  6. </head>
  7. <body>
  8. <div id="mountNode"></div>
  9. <script src="https://gw.alipayobjects.com/os/antv/pkg/_antv.g6-3.1.0/build/g6.js"></script>
  10. <script src="https://gw.alipayobjects.com/os/antv/assets/lib/jquery-3.2.1.min.js"></script>
  11. <script>
  12. const data = {
  13. nodes: [
  14. { id: '0', label: '0' },
  15. { id: '1', label: '1' },
  16. { id: '2', label: '2' },
  17. { id: '3', label: '3' },
  18. { id: '4', label: '4' }
  19. ], edges: [
  20. { source: '0', target: '1' },
  21. { source: '0', target: '2' },
  22. { source: '0', target: '3' },
  23. { source: '0', target: '4' },
  24. { source: '1', target: '2' },
  25. { source: '1', target: '3' }
  26. ]
  27. };
  28. const data2 = {
  29. nodes: [
  30. { id: '0', label: '0' },
  31. { id: '1', label: '1' },
  32. { id: '2', label: '2' }
  33. ], edges: [
  34. { source: '0', target: '1' },
  35. { source: '0', target: '2' }
  36. ]
  37. };
  38. const graph = new G6.Graph({
  39. container: 'mountNode', // String | HTMLElement,必须,容器 id 或容器本身
  40. width: 300, // Number,必须,图的宽度
  41. height: 300, // Number,必须,图的高度
  42. animate: true // Boolean,可选,切换布局时是否使用动画过度
  43. });
  44. // 读取数据和渲染
  45. graph.data(data);
  46. graph.render();
  47. // 2000 ms 后切换为允许节点重叠的 force 布局
  48. setTimeout(() => {
  49. graph.updateLayout('force'); // 参数为 String 代表布局名称
  50. }, 8000);
  51. // 4000 ms 后切换为不允许节点重叠且边长为 100 的 force 布局。
  52. setTimeout(() => {
  53. graph.updateLayout({
  54. type: 'force', // 布局名称
  55. preventOverlap: true, // 布局参数,是否允许重叠
  56. nodeSize: 40, // 布局参数,节点大小,用于判断节点是否重叠
  57. linkDistance: 100 // 布局参数,边长
  58. });
  59. }, 10000);
  60. // 6000 ms 后切换数据为 data2
  61. setTimeout(() => {
  62. graph.changeData(data2);
  63. }, 12000);
  64. </script>
  65. </body>
  66. </html>

子图布局

目前,子图布局独立与全局布局的思路,与 graph 不挂钩,直接使用实例化布局方法的方式,灌入子图数据,通过布局将位置写到相应数据中。这种机制还可供外部的全局布局使用,即使不用 G6 渲染,也可以计算节点布局后的位置。但与萧庆讨论后,决定这种方式暂时不透出够用户。在子图布局上,这种机制后续需要修改,并与全局布局思路统一( graph,controller )。

使用方法

  1. // 实例化布局
  2. const subgraphLayout = new G6.Layout['force']({
  3. center: [500, 450],
  4. });
  5. // 初始化布局,灌入子图数据
  6. subgraphLayout.init({
  7. nodes: subGraphNodes,
  8. edges: subGraphEdges,
  9. });
  10. //执行布局
  11. subgraphLayout.execute();
  12. // 图实例根据数据更新节点位置
  13. graph.positionsAnimate();