• 树的布局算法,有正交布局、径向布局、缩进布局、圆锥布局等
  • 图的布局算法,有力导布局、MDS 布局等

上面的布局算法在 D3 中都有介绍,本章仅介绍 G2 实现的布局算法和一些约定。

关系图一般使用节点-链接法绘制,使用节点表示对象,用线(或者边)表示关系。节点链接法的布局有几个原则:

  • 节点不能相互遮挡、边尽量避免交叉
  • 节点的不能移出边界,尽量提升空间利用率

使用节点-链接法绘制关系图需要两份数据,节点的数据和边的数据。由于G2中一个View只能使用一个数据源,所以在 G2 中绘制关系图需要两个 View,所以在 G2 中绘制关系图的步骤如下:

  • 声明节点和边的数据
  • 创建图表
  • 绘制边,保证节点绘制在边上面
  • XXX绘制节点
  1. var nodes = [// 节点信息:类别、ID,位置 x,y
  2. {id: '0',name: '开始',type: 'start',x: 50,y: 10},
  3. {id: '1',name: '步骤一',type: 'action',x: 50,y: 20},
  4. {id: '2',name: '步骤二',type: 'action',x: 50,y: 30},
  5. {id: '3',name: '条件',type: 'condition',x: 50,y: 40},
  6. {id: '4.1',name: '分步骤一',type: 'action',x: 40,y: 50},
  7. {id: '4.2',name: '分步骤二',type: 'action',x: 60,y: 50},
  8. {id: '5',name: '汇总',type: 'action',x: 50,y: 60},
  9. {id: '6',name: '结束',type: 'end',x: 50,y: 70}
  10. ];
  11. var edges = [
  12. {source: '0', target: '1'},
  13. {source: '1', target: '2'},
  14. {source: '2', target: '3'},
  15. {source: '3', target: '4.1'},
  16. {source: '3', target: '4.2'},
  17. {source: '4.1', target: '5'},
  18. {source: '4.2', target: '5'},
  19. {source: '5', target: '6'}
  20. ];
  21. var Stat = G2.Stat;
  22. var chart = new G2.Chart({
  23. id: 'c1',
  24. width: 800,
  25. height: 500,
  26. plotCfg: {
  27. margin: [0,0]
  28. }
  29. });
  30. var defs = {
  31. x: {min: 0,max:100},
  32. y: {min: 0, max:100},
  33. '..x': {min: 0,max:100},
  34. '..y': {min: 0,max:100}
  35. };
  36. // 首先绘制 edges,点要在边的上面
  37. // 创建单独的视图
  38. var edgeView = chart.createView();
  39. edgeView.source(edges, defs);
  40. edgeView.coord().reflect(); // 从上到下
  41. edgeView.axis(false);
  42. edgeView.tooltip(false);
  43. // Stat.link 方法会生成 ..x, ..y的字段类型,数值范围是 0-1
  44. edgeView.edge()
  45. .position(Stat.link('source*target',nodes))
  46. .color('#ccc');
  47. // 绘制节点
  48. var nodeView = chart.createView();
  49. nodeView.coord().reflect(); // 从上到下
  50. nodeView.axis(false);
  51. nodeView.source(nodes, defs);
  52. nodeView.point().position('x*y').color('steelblue')
  53. .label('name', {
  54. offset: 10
  55. })
  56. .tooltip('name');
  57. chart.render();
  • 由于节点和链接在两个视图上,所以需要统一两个视图的x,y的度量
  • 链接需要使用节点计算线(边)的起始、结束为止,所以需要统计函数 Stat.link

在 G2 中只要我们知道节点的位置和对应的边就能绘制出关系图,在 G2 中布局算法不是必须的,只要能够计算出节点的位置即可,计算出来的节点需要满足:

  • 节点中需要 x, y, id 字段,方便边的起始、结束点计算
  • 计算出来的 x, y 的值都要分布在 [0 - 1] 的范围内

G2 目前仅支持了树的正交布局,保证树的空间利用率最高

使用正交布局的步骤

  • 指定数据源
  • 使用布局算法计算树节点的位置,获取树节点之间的边
  • 分别创建边的视图和节点的视图,传入数据源
  • 渲染
  1. // 指定数据源
  2. var data = [{
  3. name: 'root',
  4. children: [{
  5. name: 'a',
  6. children: [{
  7. name: 'a1'
  8. }, {
  9. }]
  10. }, {
  11. name: 'b',
  12. children: [{
  13. name: 'b1',
  14. children: [{
  15. name: 'b11'
  16. }]
  17. }]
  18. }, {
  19. name: 'c'
  20. }]
  21. }];
  22. var layout = new G2.Layout.Tree({
  23. nodes: data
  24. });
  25. var nodes = layout.getNodes();
  26. var edges = layout.getEdges();
  27. var Stat = G2.Stat;
  28. var chart = new G2.Chart({
  29. id: 'c2',
  30. width: 800,
  31. height: 500,
  32. plotCfg: {
  33. margin: [20,0]
  34. }
  35. });
  36. var defs = {
  37. x: {min: 0,max:1},
  38. y: {min: 0, max:1}/*, 由于 ..x和..y的默认大小就是 0-1,所以可以不设置
  39. '..x': {min: 0,max:1},
  40. '..y': {min: 0,max:1}*/
  41. };
  42. // 首先绘制 edges,点要在边的上面
  43. // 创建单独的视图
  44. var edgeView = chart.createView();
  45. edgeView.source(edges, defs);
  46. edgeView.coord().reflect(); // 从上到下
  47. edgeView.axis(false);
  48. edgeView.tooltip(false);
  49. // Stat.link 方法会生成 ..x, ..y的字段类型,数值范围是 0-1
  50. edgeView.edge()
  51. .position(Stat.link('source*target',nodes))
  52. .color('#ccc');
  53. // 绘制节点
  54. var nodeView = chart.createView();
  55. nodeView.coord().reflect(); // 从上到下
  56. nodeView.axis(false);
  57. nodeView.source(nodes, defs);
  58. nodeView.point().position('x*y').color('steelblue')
  59. .label('name', {
  60. offset: 10
  61. })
  62. .tooltip('name');
  63. chart.render();

实现自己的布局算法非常容易只要满足以下条件:

  • 节点的返回值中存在 x, y 字段
  • 节点和边能够通过id 相互关联起来

使用自己布局算法的注意事项:

  • 统一边和节点视图的 x,y 度量的大小范围
  • 设定数据源
  • 在G2中使用布局算法
  1. var data = [
  2. {id: '1', name: '1'},
  3. {id: '2', name: '2'},
  4. {id: '3', name: '3'},
  5. {id: '4', name: '4'},
  6. {id: '5', name: '5'},
  7. {id: '6', name: '6'}
  8. ];
  9. {source: '0', target: '1'},
  10. {source: '1', target: '2'},
  11. {source: '2', target: '3'},
  12. {source: '3', target: '5'},
  13. {source: '3', target: '6'},
  14. {source: '4', target: '2'},
  15. {source: '5', target: '6'}
  16. ];
  17. // 自己的随机布局算法
  18. function layout(nodes) {
  19. var rst = [];
  20. nodes.forEach(function(node) {
  21. var obj = {};
  22. obj.id = node.id;
  23. obj.name = node.name;
  24. obj.x = Math.random();
  25. obj.y = Math.random(); // 使得 x,y随机的分布在0-1范围内
  26. rst.push(obj);
  27. });
  28. return rst;
  29. }
  30. // 调用布局算法
  31. var nodes = layout(data);
  32. var Stat = G2.Stat;
  33. var chart = new G2.Chart({
  34. id: 'c3',
  35. width: 800,
  36. height: 500,
  37. plotCfg: {
  38. margin: [20,20]
  39. }
  40. });
  41. var defs = {
  42. x: {min: 0,max:1},
  43. y: {min: 0, max:1}/*, 由于 ..x和..y的默认大小就是 0-1,所以可以不设置
  44. '..x': {min: 0,max:1},
  45. '..y': {min: 0,max:1}*/
  46. };
  47. // 首先绘制 edges,点要在边的上面
  48. // 创建单独的视图
  49. var edgeView = chart.createView();
  50. edgeView.source(edges, defs);
  51. edgeView.coord().reflect(); // 从上到下
  52. edgeView.axis(false);
  53. edgeView.tooltip(false);
  54. // Stat.link 方法会生成 ..x, ..y的字段类型,数值范围是 0-1
  55. edgeView.edge()
  56. .position(Stat.link('source*target',nodes))
  57. .color('#ccc');
  58. // 绘制节点
  59. var nodeView = chart.createView();
  60. nodeView.coord().reflect(); // 从上到下
  61. nodeView.axis(false);
  62. nodeView.source(nodes, defs);
  63. nodeView.point().position('x*y')
  64. .size(10)
  65. .label('name', {
  66. offset: 0
  67. })
  68. .tooltip('name');

另外,在使用过程中可以从其他框架迁移相应的布局算法。