写在前面

世界杯赛事全球瞩目,一个多月的比赛结束后,成千上万的信息向球迷们砸来,输赢、淘汰、比分、球队、国家、晋级、16强、三甲……其中关系数据更是交织如网,一团乱麻。“Wait!能不能把这些繁杂的数据组织组织,给我一个清晰的理解啊!”别急,关系数据的总结、可视化、分析,交给 G6 来帮忙。

数据

举个栗子,对于世界杯来说,人们最关注的莫过于球队之间的比赛和比分,如以下数据:

  1. {
  2. "nodes": [{
  3. "id": "Argentina",
  4. "name": "Argentina"
  5. }, {
  6. "id": "Australia",
  7. "name": "Australia"
  8. },
  9. ......
  10. ],
  11. "edges": [{
  12. "id": "0",
  13. "target": "Russia",
  14. "source": "Saudi Arabia",
  15. "target_score": 5,
  16. "source_score": 0,
  17. "directed": true
  18. }, {
  19. "id": "1",
  20. "target": "Uruguay",
  21. "source": "Egypt",
  22. "target_score": 1,
  23. "source_score": 0,
  24. "directed": true
  25. },
  26. ......
  27. ]
  28. }

效果

通过 G6 ,这个数据可以变成这样:

2018 世界杯关注度、胜负分析 - 图1

每个节点代表了一支球队,边代表了比赛,边的箭头指向获胜方,无箭头表示该场比赛打成平局。节点大小和颜色映射了该球队赢球的场次。

通过 G6 对这一关系数据的可视化,队伍之间比赛关系清晰了不少,仿佛打开了今年世界杯的上帝视角。简单分析来看:

  • 法国在这次世界杯中除了有一场小组赛和丹麦打成平局,真是战无不胜啊!难怪夺得了大力神杯。

  • 黑马克罗地亚虽然赢球场次和法国一样多,可惜最终输给了法国。

  • 较小并且颜色较深的点是在小组赛中被淘汰的球队,谁让他们赢得少呢。

  • 图上看似一个个的小“团体”是经过密切角逐的若干支球队。

  • 从另一个角度想,点的大小也是该国足球实力的体现。

  • 可是,今年的德国是怎么了呢,三场比赛竟然输了两场,连小组赛都没出线,它可是2014年世界杯的冠军啊!放眼望去,天台上满满的都是德国队球迷。

绘制图

那么,如何使用 G6 画出这样一张图呢,假设如下图的DOM场景已经搭建好了,并且有一个 id 为 ‘mountNode’ 的容器节点。让我们从简到繁,一步一步来学习。

  1. <div id = 'mountNode'></div>

2018 世界杯关注度、胜负分析 - 图2

step1: 创建图对象,数据输入

  1. var graph = null;
  2. $.getJSON('./assets/data/world-cup2018.json', data => { // 读入json数据
  3. graph = new G6.Graph({
  4. id: 'mountNode', // DOM中的容器节点
  5. width: 736, // 设置画布大小
  6. height: 600 // 设置画布大小
  7. });
  8. graph.read(data); // 将数据读入图
  9. );

现在已经将创建了图的对象,并且输入了数据。恭喜,我们看到图已经呈现在画布上了。然而现在的图没有任何布局,所有元素堆叠在一起:
2018 世界杯关注度、胜负分析 - 图3

step2:选择布局

布局是为了让图更好地被用户理解,一般来说布局后图中元素的位置能够一定程度反映元素之间的关系。在 G6 里,图的布局算法使用插件的形式来完成。首先,在 html 中引入需要的 js 文件:

  1. <script src="../build/plugin.layout.forceAtlas2.js"></script>

布局算法拥有许多参数,各个参数在算法结果上会有不同的表象。现在,我们先不传入参数,使用默认值来新建布局插件对象:

  1. const layout = new G6.Plugins['layout.forceAtlas2']();

将 layout 插件配置到图对象中,于是图对象的创建语句被修改成如下:

  1. graph = new G6.Graph({
  2. id: 'mountNode',
  3. fitView: 'autoZoom',
  4. plugins: [layout],
  5. width: 736,
  6. height: 600
  7. });
  1. <br />为了防止布局超出屏幕,使用 `fitview: 'autoZoom'` 进行自动放缩。

现在,我们可以看到图已经被合理布局,不再杂乱无章,布局后点对之间的距离基本满足图论中点对的关系远近:

2018 世界杯关注度、胜负分析 - 图4

step3: 设置映射和样式

虽然布局已经完成,但是所有的节点大小和颜色都是一样的。为了让节点大小具有更丰富的含义,并且让整张图看起来更有层次感,我们可以通过插件的形式,将球队获胜场次信息映射到节点大小和颜色上。先计算一下每个节点出度入度:

  1. const nodes = data.nodes;
  2. const edges = data.edges;
  3. for (let i = 0; i < nodes.length; i += 1) {
  4. nodes[i].degree = 0;
  5. nodes[i].indegree = 0;
  6. nodes[i].outdegree = 0;
  7. }
  8. for (let i = 0; i < edges.length; i += 1) {
  9. for (let j = 0; j < nodes.length; j += 1) {
  10. if (nodes[j].id == edges[i].source) {
  11. nodes[j].degree +=1 ;
  12. if (edges[i].directed)
  13. nodes[j].outdegree += 1;
  14. } else if (nodes[j].id == edges[i].target) {
  15. nodes[j].degree += 1;
  16. if (edges[i].directed)
  17. nodes[j].indegree += 1;
  18. }
  19. }
  20. }

图形映射

准备好数据后,可以开始使用我们的图形语法设置映射了。在 html 中引入相关 js 文件:

  1. <script src="../build/plugin.tool.mapper.js"></script>

创建映射插件:

  1. const Mapper = G6.Plugins['tool.mapper'];
  2. const nodeSizeMapper = new Mapper('node', 'indegree', 'size', [30, 50], {legendCfg: null});
  3. const nodeColorMapper = new Mapper('node', 'indegree', 'color', ['#166dac', '#008cec'], {legendCfg: null});

构建 Mapper 是传入的参数分别代表元素类型、定义域数据、映射通道、值域范围,以及图例样式。这里暂不讨论图例样式。我们将节点大小按照每个节点的入度,也就是赢球场次进行映射,最后节点大小在区间 [30,50] 内。同理,颜色在区间 [‘#166dac’, ‘#008cec’] 内。创建了 nodeSizeMapper 和 nodeColorMapper 这两个插件后,将它们配置到图到插件列表当中,方法参考 layout 插件的配置方式。

这样,我们就可以得到如下结果:
2018 世界杯关注度、胜负分析 - 图5

点边缘样式

除此之外,我们可能希望对图中元素的样式进行个性化设计,可以通过元素的回调函数来实现,例如设置点的边缘样式:

  1. graph.node({
  2. style(model) {
  3. return {
  4. stroke: '#2f4c66', // 点边缘颜色
  5. lineWidth: 5, // 点边缘粗细
  6. fillOpacity: 1, // 点填充透明度
  7. strokeOpacity: 1 // 点边缘透明度
  8. }
  9. }
  10. });

2018 世界杯关注度、胜负分析 - 图6

标签样式

同样,设置点的文字标签,并且设置文字的样式:

  1. graph.node({
  2. label(model) {
  3. return {
  4. fontFamily: 'PingFang SC', // 字体
  5. text: model.name, // 选择数据中的name属性作为文字内容
  6. fill: '#777', // 文字颜色
  7. stroke: '#777', // 文字背景颜色
  8. lineWidth: 2.5, // 文字背景宽度
  9. fontSize: 12 // 字体大小
  10. }
  11. }
  12. });

2018 世界杯关注度、胜负分析 - 图7

箭头样式

设置边的箭头,若平局则无箭头,否则箭头由败方指向胜方:

  1. graph.edge({
  2. style(model) {
  3. return{
  4. stroke: '#A3B1BF',
  5. lineWidth: 2,
  6. endArrow: model.directed
  7. };
  8. }
  9. });

2018 世界杯关注度、胜负分析 - 图8

至此,你已经能够绘制出一个完美的图了:

2018 世界杯关注度、胜负分析 - 图9

还有什么进阶骚操作,敬请关注我们的彩蛋和后续教程!

彩蛋

偷偷告诉你怎样给图加一些简单交互吧!

鼠标对图进行平移操作

在创建图对象时设置 modes 属性如下:

  1. graph = new G6.Graph({
  2. id: 'mountNode', // dom id
  3. fitView: 'autoZoom',
  4. plugins: [layout],
  5. minZoom: 0,
  6. width: 736,
  7. height: 600,
  8. modes: {
  9. default: ['panCanvas']
  10. }
  11. });

2018 世界杯关注度、胜负分析 - 图10

增加鱼眼放大镜:

首先引入需要的 js 文件:

  1. <script src="../build/plugin.tool.fisheye.js"></script>

创建半径为200的鱼眼插件对象:

  1. const fisheyePlugin = G6.Plugins['tool.fisheye']({'radius':200});

最后,参考 layout 插件配置方式,将 fisheyePlugin 配置到 graph 就大功告成啦。

2018 世界杯关注度、胜负分析 - 图11

好了,剧透到此结束,更多有趣的插件等待你挖掘~

完整代码