如果不想了解具体细节,只要看最佳的使用方式,请直接拉到文章的最后部分。
AntV G6 是一个简单、易用、完备的图可视化引擎,它在高定制能力的基础上,提供了一系列设计优雅、便于使用的图可视化解决方案。能帮助开发者搭建属于自己的图可视化、图分析、或图编辑器应用。
效果
在我们的业务中,使用 AntV G6 来开发关系图的需求,但是在有些复杂的业务需求中,可能会希望在节点中展示数据的分布情况,这就要求 G6 的节点能够支持基本的统计图表。
问题
为了提升性能,我们使用 G6 的 canvas 渲染方式,但使用 canvas 渲染时节点不支持 DOM。
G6 的性能目前已经得到了大幅度的提升,可以参考他们官网的 Demo,在 20000 数据量的情况下都可以顺畅交互:g6.antv.vision/zh/examples…
思考
G6 是底层渲染是基于 G 来实现的,而 AntV G2 也是基于 G 渲染的,那我们是不是可以把 G2 的图表嵌入到 G6 节点中呢?
顺着这个思路,我们发现 G2 中 view、shape、geometry 等支持将 G 的 group 作为容器,这个时候就可以复用 G6 中group。
实现
Step 1:引入必要的 Geometry 及 Component
在 G2 中,实现柱状图的是 Geometry 是 Interval,实现折线图的 Geometry 是 Line,另外折线上面还有圆点,使用的是 Geometry Point,因此,我们需要引入 Interval、Line 及 Point,另外还需要 x 轴来显示各个类别的名称,还需要引入 Axis 组件。
import Line from '@antv/g2/lib/geometry/line';
import Point from '@antv/g2/lib/geometry/point';
import Axis from "@antv/g2/lib/chart/controller/axis";
import Interval from "@antv/g2/lib/geometry/interval";
Step 2:注册 Geometry 及 Component
由于我们不是直接使用 G2 的 Chart,而是基于 View 来实现柱状图,因此需要先注册对应的 Geometry 及 Component,如下所示:
import { registerGeometry } from '@antv/g2/lib/chart/view';
import { registerComponentController } from '@antv/g2/lib/core';
registerGeometry("Line", Line);
registerGeometry("Point", Point);
registerGeometry("Interval", Interval);
registerComponentController("axis", Axis);
Step 3:实例化 View
由于我们是在自定义 G6 节点的时候实例化 View,所以,我们可以将 G6 中的 group 作为 View 的容器使用。
const canvas = group.get('canvas')
const backgroundGroup = group.addGroup();
const middleGroup = group.addGroup();
const foregroundGroup = group.addGroup();
const view = new View({
parent: null,
canvas,
foregroundGroup,
middleGroup,
backgroundGroup,
padding: 5,
visible: true,
region: {
start: {
x: 0.01,
y: 0.2
},
end: {
x: 0.8,
y: 0.35
}
},
});
view.data(cfg.trendData);
view
.point()
.position('月份*月均降雨量')
.color('name');
view
.line()
.position("月份*月均降雨量")
.color("name")
.shape('dash');
view
.interval()
.position("月份*月均降雨量")
.color("name")
.adjust([
{
type: 'dodge',
marginRatio: 0,
},
]);
view.legend(false);
view.axis('月均降雨量', false)
view.render();
在这一步有以下几点需要特别注意:
- region 用于控制柱状图的显示范围,x 和 y 的取值都在 [0-1] 范围内;
- region 中 x 和 y 的取值根据 canvas 宽度和高度的来确定,而不是节点 keyShape 的宽高。
Step 4:自定义节点
在完成了前三步以后,自定义带有柱状图的节点就相对比较简单了,按照 G6 中的要求来实现即可。
G6.registerNode('node-with-multchart', {
draw(cfg, group) {
const canvas = group.get('canvas')
const keyShape = group.addShape('rect', {
attrs: {
x: 0,
y: 0,
width: 400,
height: 200,
fill: cfg.style.fill
}
})
group.addShape('rect', {
attrs: {
x: 0,
y: 0,
width: 400,
height: 40,
fill: '#69c0ff'
}
})
group.addShape('text', {
attrs: {
text: '浏览申请完成率',
x: 10,
y:25,
fontSize: 14,
fill: '#fff'
}
})
group.addShape('text', {
attrs: {
text: '2020-06-07 ~ 2020-06-14 | 均值',
x: 20,
y: 70,
fontSize: 13,
fill: '#8c8c8c'
}
})
group.addShape('text', {
attrs: {
text: '8.8%',
x: 20,
y: 110,
fontSize: 30,
fill: '#000'
}
})
const backgroundGroup = group.addGroup();
const middleGroup = group.addGroup();
const foregroundGroup = group.addGroup();
const view = new View({
parent: null,
canvas,
foregroundGroup,
middleGroup,
backgroundGroup,
padding: 5,
visible: true,
region: {
start: {
x: 0.01,
y: 0.2
},
end: {
x: 0.8,
y: 0.35
}
},
});
view.data(cfg.trendData);
view
.point()
.position('月份*月均降雨量')
.color('name');
view
.line()
.position("月份*月均降雨量")
.color("name")
.shape('dash');
view
.interval()
.position("月份*月均降雨量")
.color("name")
.adjust([
{
type: 'dodge',
marginRatio: 0,
},
]);
view.legend(false);
view.axis('月均降雨量', false)
view.render();
return keyShape
},
update: null
}, 'single-node')
在这一步需要注意以下几点:
- 将柱状图作为节点的一部分,而不要作为 keyShape 来使用;
- 建议继承 G6 内置的 single-node,这样就可以使用 G6 中的状态管理的能力;
- 关于节点的更新,有两种方式来做:
- 将 update 方法设置为 null 或 undefined,这样每次都会执行 draw 方法重绘;
- 实现 update 方法,在 update 方法中调用 view 的changeData 来更新数据,这样就需要在 draw 时将 view 对象存储下来,具体的实现如下所示:
G6.registerNode('node-with-interval', {
draw(cfg, group) {
const canvas = group.get('canvas')
const keyShape = group.addShape('rect', {
attrs: {
x: 0,
y: 0,
width: 400,
height: 200,
fill: cfg.style.fill
}
})
const view = new View({
// ...
});
view.render();
// 存储 view 对象,在 update 中使用
keyShape.set('intervalView', view)
return keyShape
},
update(cfg, item) {
const keyShape = item.getKeyShape()
const view = keyShape.get('intervalView')
view.changeData(cfg.trendData)
// keyShape 及其他部分的更新和 G6 普通节点更新方式完全一样
}
}, 'single-node')
使用
使用 G6 自定义节点方式定义了名称为 node-with-interval 的节点以后,使用的时候和 circle、rect 等普通节点的方式完全相同,可以在实例化 Graph 时通过 defaultNode 的 type 字段指定,也可以在数据中通过 type 字段指定。
const graph = new G6.Graph({
// 省略其他配置......
defaultNode: {
type: 'node-with-interval'
}
});
使用上面的方式固然可以让 G6 的节点支持 G2 的图表,但实现起来过于复杂,需要我们对 G2 的源码有一定的了解,对于业务开发同学来说特别不友好。那有没有更简单的方法呢?
我们发现 G6 在 3.7.0 版本发布 以后,官网上面多了几个自定义节点的案例,点进去一看,震惊!竟然是节点中支持折线图、柱状图、饼图等的案例,这不正是我们上面介绍的吗?
最佳实现
为了让业务开发者更方便地在节点中支持统计图表,AntV G6 官方提供了 @antv/chart-node-g6 包,使用这个包,我们就再也不用去关心 G2 中的细节了,具体的使用方式清晰明了。
import G6 from '@antv/g6';
import Chart from '@antv/chart-node-g6';
G6.registerNode(
'node-with-interval',
{
draw(cfg, group) {
const keyShape = group.addShape('rect', {
attrs: {
x: 0,
y: 0,
width: 400,
height: 200,
fill: cfg.style.fill,
},
});
const view = new Chart({
group,
padding: 1,
width: 360,
height: 70,
x: 20,
y: 100
});
view.data(cfg.trendData);
view.interval().position('genre*sold').color('genre');
view.legend('genre', false);
view.scale({
genre: {
alias: '游戏种类', // 列定义,定义该属性显示的别名
},
sold: {
alias: '销售量',
},
});
view.axis('sold', false);
// 极坐标下的柱状图
// view.coordinate('polar');
view.render();
keyShape.set('intervalView', view);
return keyShape;
},
update(cfg, item) {
const keyShape = item.getKeyShape();
const view = keyShape.get('intervalView');
view.changeData(cfg.trendData);
},
},
'single-node',
);
具体效果就下图所示:
更多的案例请参考 AntV G6 官网。
AntV G6 是一款开源的图可视化引擎,专注于图可视化及图分析领域。
欢迎关注和 star G6 GitHub:github.com/antvis/G6
官网:g6.antv.vision/zh/