树是图中一种特殊的数据结构。其特殊的性质、优美的结构,使得树形图在我们日常中广泛使用。比如,世界杯淘汰赛的晋级图,就是一个典型的树形数据可视化的案例。

2018 世界杯对阵图 - 图1

下面我们将与大家探讨,如何使用 G6 快速实现这幅图。

场景准备

数据准备

  1. {
  2. "roots": [{
  3. "teamA": "法国",
  4. "flagA": "https://gw.alipayobjects.com/zos/rmsportal/yHYaCVIPRwEmmjbNqJjW.jpg",
  5. "teamB": "克罗地亚",
  6. "flagB": "https://gw.alipayobjects.com/zos/rmsportal/LJoAlBjTxagRLoNgSVmU.jpg",
  7. "scoreA": 4,
  8. "scoreB": 2,
  9. "children": [{
  10. "teamA": "法国",
  11. "flagA": "https://gw.alipayobjects.com/zos/rmsportal/yHYaCVIPRwEmmjbNqJjW.jpg",
  12. "teamB": "比利时",
  13. "flagB": "https://gw.alipayobjects.com/zos/rmsportal/tUwmQwmljZAvTXtljXcV.jpg",
  14. "scoreA": 1,
  15. "scoreB": 0,
  16. "children": [
  17. ……

在开始绘制树形图之前,我们先设置好一个 dom,并配上底图:

  1. <style>
  2. #mountNode {
  3. width: 760px;
  4. height: 744px;
  5. background: url(https://gw.alipayobjects.com/zos/rmsportal/OSujrQtOvnOJVzRLmfIP.png);
  6. }
  7. </style>
  8. <div id='mountNode'></div>

2018 世界杯对阵图 - 图2

自定义节点-竞赛卡片

我们先来注册一个 node ,给它取个名字,就叫”card”(卡片)好了。

  1. G6.registerNode('card', {});

draw 是自定义图项的最小接口,我们需要通过操作绘图引擎 G 来生成竞赛卡片。

2018 世界杯对阵图 - 图3

  1. draw(item) {
  2. const group = item.getGraphicGroup();
  3. const model = item.getModel();
  4. const scoreTextStyle = {
  5. textAlign: 'center',
  6. fontWeight: 900,
  7. fontSize: 14,
  8. fill: '#000'
  9. };
  10. const teamNameTextStyle = {
  11. textAlign: 'center',
  12. fontSize: 12,
  13. fill: 'rgba(0, 0, 0, 0.45)'
  14. };
  15. const width = 134;
  16. const height = 60;
  17. const keyShape = group.addShape('rect', {
  18. attrs: {
  19. x: 0,
  20. y: 0,
  21. width,
  22. height,
  23. fill: '#fff',
  24. radius: 4,
  25. }
  26. });
  27. group.addShape('image', {
  28. attrs: {
  29. x: 8,
  30. y: 8,
  31. width: 40,
  32. height: 24,
  33. img: model.flagA
  34. }
  35. });
  36. group.addShape('image', {
  37. attrs: {
  38. x: 86,
  39. y: 8,
  40. width: 40,
  41. height: 24,
  42. img: model.flagB
  43. }
  44. });
  45. group.addShape('text', {
  46. attrs: {
  47. ...teamNameTextStyle,
  48. x: 28,
  49. y: 52,
  50. text: model.teamA,
  51. }
  52. });
  53. group.addShape('text', {
  54. attrs: {
  55. ...teamNameTextStyle,
  56. x: 106,
  57. y: 52,
  58. text: model.teamB,
  59. }
  60. });
  61. group.addShape('text', {
  62. attrs: {
  63. ...scoreTextStyle,
  64. x: 67,
  65. y: 52,
  66. text: model.scoreA + ' : '+ model.scoreB,
  67. }
  68. });
  69. return keyShape;
  70. }

注意 :在自定义图形时,设置坐标时,我们只需要考虑自身坐标系,而不需要考虑世界坐标系。

根据 G6 对锚点的定义,设置锚点

2018 世界杯对阵图 - 图4

2018 世界杯对阵图 - 图5

  1. anchor: [
  2. [0, 0.5], // 左中
  3. [1, 0.5], // 右中
  4. [0.5, 0], // 上中
  5. [0.5, 1] // 下中
  6. ]

选择布局

G6 中内置了若干种树图布局,不难看出这是,一种竖直方向紧凑树的布局。

2018 世界杯对阵图 - 图6

  1. const layout = new G6.Layouts.CompactBoxTree({
  2. direction: 'V',
  3. });

选择合适的边

边路由是图可视化领域中的一个难点,为了屏蔽这部分的复杂度,我们沉淀一些常见的边路由图形,供大家使用。

  1. require('@antv/g6/build/plugin.edge.polyline');

引入该插件,会在 G6 中注册 polyline (直角) , polyline-round (圆角折线)两种边路由:

2018 世界杯对阵图 - 图72018 世界杯对阵图 - 图8

绘制图

有了上面储备的原料,我们已经基本上能画出这幅对阵图了。

2018 世界杯对阵图 - 图9

  1. const tree = new G6.Tree({
  2. container: 'mountNode',
  3. width: 760,
  4. height: 744,
  5. fitView: 'cc',
  6. layout,
  7. });
  8. tree.node({
  9. shape: 'card'
  10. });
  11. tree.edge({
  12. shape: 'polyline-round',
  13. style: {
  14. stroke: 'white',
  15. lineWidth: 1,
  16. strokeOpacity: 1
  17. }
  18. });
  19. tree.read(data);

完整代码