写在前面
世界杯赛事全球瞩目,一个多月的比赛结束后,成千上万的信息向球迷们砸来,输赢、淘汰、比分、球队、国家、晋级、16强、三甲……其中关系数据更是交织如网,一团乱麻。“Wait!能不能把这些繁杂的数据组织组织,给我一个清晰的理解啊!”别急,关系数据的总结、可视化、分析,交给 G6 来帮忙。
数据
举个栗子,对于世界杯来说,人们最关注的莫过于球队之间的比赛和比分,如以下数据:
{
"nodes": [{
"id": "Argentina",
"name": "Argentina"
}, {
"id": "Australia",
"name": "Australia"
},
......
],
"edges": [{
"id": "0",
"target": "Russia",
"source": "Saudi Arabia",
"target_score": 5,
"source_score": 0,
"directed": true
}, {
"id": "1",
"target": "Uruguay",
"source": "Egypt",
"target_score": 1,
"source_score": 0,
"directed": true
},
......
]
}
效果
通过 G6 ,这个数据可以变成这样:
每个节点代表了一支球队,边代表了比赛,边的箭头指向获胜方,无箭头表示该场比赛打成平局。节点大小和颜色映射了该球队赢球的场次。
通过 G6 对这一关系数据的可视化,队伍之间比赛关系清晰了不少,仿佛打开了今年世界杯的上帝视角。简单分析来看:
法国在这次世界杯中除了有一场小组赛和丹麦打成平局,真是战无不胜啊!难怪夺得了大力神杯。
黑马克罗地亚虽然赢球场次和法国一样多,可惜最终输给了法国。
较小并且颜色较深的点是在小组赛中被淘汰的球队,谁让他们赢得少呢。
图上看似一个个的小“团体”是经过密切角逐的若干支球队。
从另一个角度想,点的大小也是该国足球实力的体现。
可是,今年的德国是怎么了呢,三场比赛竟然输了两场,连小组赛都没出线,它可是2014年世界杯的冠军啊!放眼望去,天台上满满的都是德国队球迷。
绘制图
那么,如何使用 G6 画出这样一张图呢,假设如下图的DOM场景已经搭建好了,并且有一个 id 为 ‘mountNode’ 的容器节点。让我们从简到繁,一步一步来学习。
<div id = 'mountNode'></div>
step1: 创建图对象,数据输入
var graph = null;
$.getJSON('./assets/data/world-cup2018.json', data => { // 读入json数据
graph = new G6.Graph({
id: 'mountNode', // DOM中的容器节点
width: 736, // 设置画布大小
height: 600 // 设置画布大小
});
graph.read(data); // 将数据读入图
);
现在已经将创建了图的对象,并且输入了数据。恭喜,我们看到图已经呈现在画布上了。然而现在的图没有任何布局,所有元素堆叠在一起:
step2:选择布局
布局是为了让图更好地被用户理解,一般来说布局后图中元素的位置能够一定程度反映元素之间的关系。在 G6 里,图的布局算法使用插件的形式来完成。首先,在 html 中引入需要的 js 文件:
<script src="../build/plugin.layout.forceAtlas2.js"></script>
布局算法拥有许多参数,各个参数在算法结果上会有不同的表象。现在,我们先不传入参数,使用默认值来新建布局插件对象:
const layout = new G6.Plugins['layout.forceAtlas2']();
将 layout 插件配置到图对象中,于是图对象的创建语句被修改成如下:
graph = new G6.Graph({
id: 'mountNode',
fitView: 'autoZoom',
plugins: [layout],
width: 736,
height: 600
});
<br />为了防止布局超出屏幕,使用 `fitview: 'autoZoom'` 进行自动放缩。
现在,我们可以看到图已经被合理布局,不再杂乱无章,布局后点对之间的距离基本满足图论中点对的关系远近:
step3: 设置映射和样式
虽然布局已经完成,但是所有的节点大小和颜色都是一样的。为了让节点大小具有更丰富的含义,并且让整张图看起来更有层次感,我们可以通过插件的形式,将球队获胜场次信息映射到节点大小和颜色上。先计算一下每个节点出度入度:
const nodes = data.nodes;
const edges = data.edges;
for (let i = 0; i < nodes.length; i += 1) {
nodes[i].degree = 0;
nodes[i].indegree = 0;
nodes[i].outdegree = 0;
}
for (let i = 0; i < edges.length; i += 1) {
for (let j = 0; j < nodes.length; j += 1) {
if (nodes[j].id == edges[i].source) {
nodes[j].degree +=1 ;
if (edges[i].directed)
nodes[j].outdegree += 1;
} else if (nodes[j].id == edges[i].target) {
nodes[j].degree += 1;
if (edges[i].directed)
nodes[j].indegree += 1;
}
}
}
图形映射
准备好数据后,可以开始使用我们的图形语法设置映射了。在 html 中引入相关 js 文件:
<script src="../build/plugin.tool.mapper.js"></script>
创建映射插件:
const Mapper = G6.Plugins['tool.mapper'];
const nodeSizeMapper = new Mapper('node', 'indegree', 'size', [30, 50], {legendCfg: null});
const nodeColorMapper = new Mapper('node', 'indegree', 'color', ['#166dac', '#008cec'], {legendCfg: null});
构建 Mapper 是传入的参数分别代表元素类型、定义域数据、映射通道、值域范围,以及图例样式。这里暂不讨论图例样式。我们将节点大小按照每个节点的入度,也就是赢球场次进行映射,最后节点大小在区间 [30,50] 内。同理,颜色在区间 [‘#166dac’, ‘#008cec’] 内。创建了 nodeSizeMapper 和 nodeColorMapper 这两个插件后,将它们配置到图到插件列表当中,方法参考 layout 插件的配置方式。
这样,我们就可以得到如下结果:
点边缘样式
除此之外,我们可能希望对图中元素的样式进行个性化设计,可以通过元素的回调函数来实现,例如设置点的边缘样式:
graph.node({
style(model) {
return {
stroke: '#2f4c66', // 点边缘颜色
lineWidth: 5, // 点边缘粗细
fillOpacity: 1, // 点填充透明度
strokeOpacity: 1 // 点边缘透明度
}
}
});
标签样式
同样,设置点的文字标签,并且设置文字的样式:
graph.node({
label(model) {
return {
fontFamily: 'PingFang SC', // 字体
text: model.name, // 选择数据中的name属性作为文字内容
fill: '#777', // 文字颜色
stroke: '#777', // 文字背景颜色
lineWidth: 2.5, // 文字背景宽度
fontSize: 12 // 字体大小
}
}
});
箭头样式
设置边的箭头,若平局则无箭头,否则箭头由败方指向胜方:
graph.edge({
style(model) {
return{
stroke: '#A3B1BF',
lineWidth: 2,
endArrow: model.directed
};
}
});
至此,你已经能够绘制出一个完美的图了:
还有什么进阶骚操作,敬请关注我们的彩蛋和后续教程!
彩蛋
偷偷告诉你怎样给图加一些简单交互吧!
鼠标对图进行平移操作
在创建图对象时设置 modes 属性如下:
graph = new G6.Graph({
id: 'mountNode', // dom id
fitView: 'autoZoom',
plugins: [layout],
minZoom: 0,
width: 736,
height: 600,
modes: {
default: ['panCanvas']
}
});
增加鱼眼放大镜:
首先引入需要的 js 文件:
<script src="../build/plugin.tool.fisheye.js"></script>
创建半径为200的鱼眼插件对象:
const fisheyePlugin = G6.Plugins['tool.fisheye']({'radius':200});
最后,参考 layout 插件配置方式,将 fisheyePlugin 配置到 graph 就大功告成啦。
好了,剧透到此结束,更多有趣的插件等待你挖掘~