持续更新中...
目录
一、简单介绍
以下有什么不恰当的地方还请多多指教,因为公司业务需求,被迫看了浅学习了一下antv-g6,之前一直使用Echarts,感觉Echarts就很好用,可是看过g6之后,觉得确实很漂亮,而且也能实现一些业务中比较变态的要求。接下来我就从我刚入手的角度,谈一下自己的心得体会。
二、安装引用
Step 1: 使用命令行在项目目录下执行以下命令:
npm install --save @antv/g6
Step 2: 在需要用的 G6 的 JS 文件中导入:
import G6 from '@antv/g6';
2. 在 HTML 中使用 CDN 引入
// version <= 3.2
<script src="https://gw.alipayobjects.com/os/antv/pkg/_antv.g6-{$version}/build/g6.js"></script>
// version >= 3.3
<script src="https://gw.alipayobjects.com/os/lib/antv/g6/{$version}/dist/g6.min.js"></script>
// version >= 4.0
<script src="https://gw.alipayobjects.com/os/lib/antv/g6/4.3.11/dist/g6.min.js"></script>
三、树图G6.TreeGraph
这里直接用自己写的一个简单的demo,通过各个属性来延伸一下其他的属性。这里对每个属性进行了简单的注释,当然也不是全部的,只是一些最常用到的属性,其他具体的属性可以看antv的官方文档。
Step1:创建一个容器
<div id="g6Box"></div>
Step2:数据结构(数据结构就是tree结构)
data: {
id: "1",
label: '老大(最外层)',
"children": [
{
"id": "1-1",
label: '第二层1',
},
{
"id": "1-2",
label: '第二层2',
},
{
"id": "1-3",
label: '第二层3',
},
{
"id": "1-4",
label: '第二层4',
},
{
"id": "1-5",
label: '第二层5',
"children": [
{
"id": "1-5-1",
label: '第三层1',
children: [
{
"id": "1-5-1-1",
label: '第四层1'
},
{
"id": "1-5-1-2",
label: '第四层2'
},
{
"id": "1-5-1-3",
label: '第四层3'
}
]
},
{
"id": "1-5-2",
label: '第三层2',
children: [
{
"id": "1-5-2-1",
label: '第四层4'
},
{
"id": "1-5-2-2",
label: '第四层5'
}
]
},
{
"id": "1-5-3",
label: '第三层3',
children: [
{
"id": "1-5-3-1",
label: '第四层6'
},
{
"id": "1-5-3-2",
label: '第四层7'
},
{
"id": "1-5-3-3",
label: '第四层8'
},
{
"id": "1-5-3-4",
label: '第四层9'
},
{
"id": "1-5-3-5",
label: '第四层10'
}
]
},
{
"id": "1-5-4",
label: '第三层4',
children:[
{
"id": "1-5-4-1",
label: '第四层11'
},
{
"id": "1-5-4-2",
label: '第四层12'
},
{
"id": "1-5-4-3",
label: '第四层13'
},
{
"id": "1-5-4-4",
label: '第四层14'
}
]
}
]
},
{
"id": "1-6",
label: '第二层6'
}
]
}
Step3:节点、线的修改,数据配置,渲染
initTree (data) {
// 定义画布的宽高
const width = document.getElementById('g6Box').scrollWidth || 800;
const height = document.getElementById('g6Box').scrollHeight || 800;
// 实例化G6
// 因为我们用的是树图,所以这里是G6.TreeGraph(),还有其他,像是普通图的配置G6.Graph(),一般y用的比较多的就像是树图这种,还有组织架构图一类的。
const graph = new G6.TreeGraph({
// 图的 DOM 容器,对应上面我们定义的id
container: 'g6Box',
width,
height,
// 是否开启画布自适应。开启后图自动适配画布大小。
fitView: true,
// fitView 为 true 时生效。图适应画布时,指定四周的留白。可以是数组,fitViewPadding: [ 20, 40, 50, 20 ]
fitViewPadding: 10,
// v3.5.1 后支持。开启后,图将会被平移,图的中心将对齐到画布中心,但不缩放。优先级低于 fitView。
fitCenter: true,
// 最小缩放比例
// minZoom: 1.5,
// 最大缩放比例
maxZoom: 1.5,
// 指定边是否连入节点的中心
linkCenter: false,
// 设置画布的交互模式
modes: {
default: [
{
// 只适用于树图,展开或收起子树
type: 'collapse-expand',
// trigger:收起和展开节点分组的方式。支持 'click' 和 'dblclick' 两种方式。默认为 'dblclick',即双击。
trigger: 'click',
},
// 拖拽画布
'drag-canvas',
// 缩放画布
'zoom-canvas',
],
},
// 配置节点的属性
defaultNode: {
// 节点类型,cicle:圆形,rect:矩形,ellipse:椭圆,diamond:菱形,triangle:三角形,star:五角星,image:图片,modelRect:卡片
type: 'rect',
// size 设置矩形的长和宽
size: [120, 34],
// 指定边连入节点的连接点的位置,可以为空,具体可以看一下官网是通过0、0.5、1来控制哪个点的。
anchorPoints: [
[0, 0.5],[1, 0.5]
],
// 节点样式
style: {
// 节点填充色
fill: '#DDE2E9',
// 节点的描边颜色。
stroke: '',
// 阴影颜色
shadowColor: '#f00',
// 阴影范围
shadowBlur: 5,
// 鼠标经过是的形状,跟css是一样的。
cursor:'pointer',
// 圆角
radius: 4,
},
// 配置节点中的文字。
labelCfg: {
// 节点文字位置
// position: 'top',
// 偏移量
// offset: 5,
// 标签的样式属性。
style: {
// 文本颜色
fill: '#535D79',
// 文本字体大小
fontSize: 14,
},
},
// 文本文字 (如果自定义节点了或者用graph.node()配置了节点,这里不生效,因为那两种优先级更高,下面的边也是一样)
label: 'node-label'
},
// 配置边的属性
defaultEdge: {
// 指定边的类型,可以是内置边的类型名称,也可以是自定义边的名称。
// line:直线,polyline:折线,arc:圆弧线,quadratic:二阶贝塞尔曲线,cubic:三阶贝塞尔曲线,cubic-vertica:垂直方向的三阶贝塞尔曲线,不考虑用户从外部传入的控制点。cubic-horizontal:水平方向的三阶贝塞尔曲线,不考虑用户从外部传入的控制点。loop:自环
type: 'cubic-horizontal',
// 边的样式属性
style: {
// 边的颜色
stroke: '#A3B1BF',
// 边的宽度
lineWidth: 5,
// 边是否带箭头
endArrow:true,
},
// 配置边上文本的属性
labelCfg: {
// 文本相对于边的位置,目前支持的位置有:'start','middle','end'。默认为'middle'。
position: 'middle',
// 标签文字是否跟随边旋转
autoRotate: true,
style: {
// 文本颜色
fill: '#f00'
}
},
// 文本文字 (如果自定义节点了或者用graph.edge()配置了节点,这里不生效,因为那两种优先级更高)
label: '边'
},
// 布局配置项
layout: {
// 布局名称,这个可就太多了,这个只是树图结构中的一种。比如还有compactBox:紧凑树布局,dendrogram:生态树布局,indented:缩紧树布局。
type: 'mindmap', // 脑图树布局
direction: 'H', // H / V / LR / RL / TB / BT 这些是控制节点分布位置,从左往右、从右往左、从中间往上下延伸、从中间往左右延伸...具体可以看官网
// 节点 id 的回调函数
getId: function getId(d) {
return d.id;
},
// 下面都是一些控制节点与节点间距离的回调函数,具体可以试着修改一下值。
// 节点高度的回调函数
getHeight: function getHeight() {
return 16;
},
// 节点宽度的回调函数
getWidth: function getWidth() {
return 16;
},
// 节点纵向间距的回调函数
// getVGap: function getVGap() {
// return 50;
// },
// 节点横向间距的回调函数
getHGap: function getHGap(val) {
return 100;
},
},
// 动画属性
animate: true,
});
// 设置各个节点样式及其他配置,该方法必须在 render 之前调用,否则不起作用,必须要 return。
// 如果节点不满足自己的需求,可以在这里设置想要的节点样式,这只是其中的一种,后面还会介绍功能更加全面的自定义节点。
graph.node(function (node) {
if (node.id === '1-6' || node.id === '1-1') {
return {
labelCfg: {
style: {
fill: '#fff',
},
},
style: {
fill: '#946DFF',
}
}
}
return true
});
// 设置边的属性及其他配置,跟上面设置节点一样,这里设置了,上面defaultEdge里面设置的边不管写什么都不会生效,因为这个的优先级要比上面的defaultEdge优先级高。
// 也可以通过判断来实现不同边的样式、文字等,后面也会介绍更加全面的自定义边。
graph.edge(function(node) {
return {
label: '边'
}
})
// 初始化的图数据
graph.data(data);
// 根据提供的数据渲染视图。
graph.render();
// 让画布内容适应视口
graph.fitView();
},
四、自定义节点
本以为直接按照官网的图标示例,再稍微看一下一些属性就能搞定,但是后来发现不只是这一种简单的可以直接实现的g6图,有个不仅仅只是简单的节点里面是文字,有的节点里面还会有很多不同文字、icon图标、图片等等。这就需要能实现更多功能的自定义节点。(自定义节点和自定义边基本一样,这里具体介绍自定义节点的使用。)
先来看一下官方给的自定义节点说明:
先简单介绍一下,写的其实很详细了,'nodeName'就是自定义节点的名称,要跟下面defaultNode中的type保持一致,这里写什么,下面就要写什么,后面会在代码中体现的。draw(cfg, group){ },绘制节点的主要就用这个东西,也主要介绍这个的用法,剩下的那几个我也没用到,主要是当数据源改变的时候,更新节点的吧。具体没用过...
// 自定义节点
G6.registerNode('rectNode',
{
draw: (cfg, group) => {
// 节点宽度
function nodeWidth (node) {
if (node.id === '1') {
return 200
}
return 150
}
// 节点高度
function nodeHeight (node) {
if (node.id === '1') {
return 40
}
return 70
}
// 最外层的节点--盒子
// group.addShape 就是增加一个节点,第一个值可选值有circle、rect、ellipse、polygon、fan、image、marker、path、text、dom,用的最多的应该就是circle、rect、path、text,这里dom比较特殊,后面单独讲一下。
const shape = group.addShape('rect', {
draggable: true, // 该图形分组是否允许被拖拽
attrs: {
// 这是控制节点的宽和高,因为不是所有的节点不是一样大的,就写了两个函数来控制一下,这里可以根据自己的要求来自己用函数来控制。
width: nodeWidth(cfg),
height: nodeHeight(cfg),
fill: cfg.id === '1' ? '#4570FF' : '#fff', // 节点填充色
stroke: '#B1BDCE', // 边框颜色
radius: 4, // 圆角
}
})
// 自定义节点中的文字,因为是文字,所以是type是text
group.addShape('text', {
attrs: {
textAlign: 'center', // 文字相对于节点的位置
// x,y是控制文字的更加精确的位置
y: cfg.id === '1' ? 25 : 40,
x: cfg.id === '1' ? 110 : 70,
lineHeight: 15, // 自定义文字行高
fontSize:11, // 文字大小
text: cfg.label, // 根据后端字段名来显示的文字,这个上下文中的cfg就是每个节点中的所有属性。
fill: cfg.id === '1' ? '#fff' : '#000' // 文字颜色
}
})
// 添加dom,比较特殊,后面讲
group.addShape('dom',{
attrs: {
x: -cfg.x + 10,
y: -cfg.y - 50,
width: 20,
height: 20,
html:cfg.id === '1' ? '<em class="iconfont iconshouye-shouye" style="color: #fff;" />': ''
},
name: 'image-shape',
})
return shape
}
})
const graph = new G6.TreeGraph({
defaultNode: {
type: 'rectNode', // 这个名要跟上面自己定义的保持一致,之前写过的下面的一些控制节点的属性也可以直接在上面自定义节点里面写。
},
})
五、自定义边
自定义边和自定义节点基本是一致的,只要能懂上面的自定义节点,照猫画虎是一样的,直接上代码。
// 自定义边
G6.registerEdge('regEdge',{
draw: (cfg, group) => {
const startPoint = cfg.startPoint;
const endPoint = cfg.endPoint;
const shape = group.addShape('path', {
attrs: {
stroke: '#B1BDCE', // 边的颜色
// 这个path是来调节边的高度和长度的,因为这里我用的是折线,所以它有个起始点、结束点、还有两个转折的地方,就是一点一点的调的,哈哈。
path: [
["M", startPoint.x, startPoint.y],
["L", startPoint.x, (startPoint.y + endPoint.y) / 2.3],
["L", endPoint.x, (startPoint.y + endPoint.y) / 2.3],
["L", endPoint.x, endPoint.y],
]
},
name: 'path-shape',
});
// 这里可以控制边上的描述文字,以及在边上的位置,下面也是一样,因为有两个描述信息,具体可以看官网更加详细的属性配置。
group.addShape('text',{
attrs: {
text: cfg.targetNode['_cfg'].model.text,
x: endPoint.x -5,
y: endPoint.y - startPoint.y/2 - 10,
fill: '#4570FF',
textAlign: 'right',
},
name: 'text-shape',
})
group.addShape('text',{
attrs: {
text: cfg.targetNode['_cfg'].model.rate,
x: endPoint.x +5,
y: endPoint.y - startPoint.y/2 - 10,
fill: '#4570FF',
textAlign: 'left',
},
name: 'text-shape',
})
return shape;
},
})
const graph = new G6.TreeGraph({
defaultNode: {
type: 'regEdge', // 这个名要跟上面自己定义的保持一致,之前写过的下面的一些控制边的属性也可以直接在上面自定义边里面写。
},
})
六、自定义节点中的特殊类型—dom
项目中遇到在节点中要加入icon图标,就是阿里巴巴图标库中的那种图标,然后要想加这种就要直接给它加个dom,但是js中又无法直接写个标签给它加上,后来才发现自定义节点中有个dom类型,然后就觉得这个问题解决了,但是加上不好使,又去antvg6去看官方文档,人家清清楚楚的写着要加一个属性才能用这个自定义节点的dom...
// 还记得上面自定义节点中的dom节点嘛,如果不加下面这行代码,是报错的。
const graph = new G6.TreeGraph({
renderer: 'svg',
})
除了用自定义节点dom来实现,其实官方有自带的icon属性,不过也不是全部的节点都可以用的,一起来看一下。
官方给的节点类型是有下面这些:
每一个节点类型都有对应的配置项说明,只有官方让用icon属性的才可以用icon图标,总结了一下能用的有circle、ellipse、diamond、triangle、star、dount这几种类型
// 定义节点
graph.node(function(node) {
type: '', // circle、ellipse、diamond、triangle、star、dount这里面的才可以
icon: {
show: true, // 是否开启
fontFamily: 'iconfont', // 对应css里面的font-family: "iconfont"
text: '\ue73b', // 对应iconfont.css 里面的content,注意加u
fill: '#fff', // 填充色
width: 45,
height: 45,
fontSize: 25 // icon大小
}
})
七、同一级数据控制左(上)右(下)的节点个数
当children中有很多同一级的数据时,我们又想让它们左右或者上下分类显示,之前想的是用自定义节点来实现,但是要控制每一个的位置,感觉不好实现就没尝试,最后通过看文档,找到了一个切合这个实际的属性,来看一下代码。
// 数据结构
{
id: '1',
label: '春天花花幼儿园分园',
children: [
{
id: '1-1',
label: '我在左边',
state: 'child-left'
},
{
id: '1-2',
label: '我在左边',
state: 'child-left'
},
{
id: '1-3',
label: '我在左边',
state: 'child-left'
},
{
id: '1-4',
label: '我是右边的'
},
{
id: '1-5',
label: '我是右边的'
},
{
id: '1-6',
label: '我是右边的'
},
{
id: '1-7',
label: '我在左边',
state: 'child-left'
},
{
id: '1-8',
label: '我是右边的'
},
{
id: '1-9',
label: '我是右边的'
},
{
id: '1-10',
label: '我是右边的'
}
]
}
// 这里其他的属性还是参考上面,只写控制左右的属性
const graph = new G6.TreeGraph({
layout: {
type: 'mindmap',
direction: 'H',
getId: function getId (d) {
return d.id
},
getHeight: function getHeight () {
return 20
},
getWidth: function getWidth () {
return 10
},
getVGap: () => {
return 15
},
getHGap: function getHGap () {
return 160
},
// 这个getSide就是控制节点位置的属性了,通过数据结构中定义的值做判断,来控制左右,注意的是这个官方写的只能return 'left'和'right',当我们的树结构是竖着的呢?难道是用top和bottom?这里我也试过了,用top和bottom是不好使的,因为人家官方确确实实的只有left和right,通过尝试,其实left就对应top,right对应bottom,所以要控制节点在上面就写left,在下面就写right。
getSide: (node) => {
if(node.data.state === 'child-left') {
return 'left'
}
return 'right'
}
},
})
这个getSide就是控制节点位置的属性了,通过数据结构中定义的值做判断,来控制左右,注意的是这个官方写的只能return 'left'和'right',当我们的树结构是竖着的呢?难道是用top和bottom?这里我也试过了,用top和bottom是不好使的,因为人家官方确确实实的只有left和right,通过尝试,其实left就对应top,right对应bottom,所以要控制节点在上面就写left,在下面就写right。
八、从外面拿到graph实例对象,并实现操作方法
从官方文档可以看到,g6的方法乱七八糟一大堆,在绘制canvas之前,我们可以运用里面的方法实现自己想要的效果,但是当外面有个独立的按钮,我们想在外面操作里面的各个节点该怎么做呢?接下来就一起来看一下吧。
<button @click="clickBtn">点击</button>
data() {
return {
// 随便自定义一个名字
canvasGraph: '',
}
},
methods:{
clickBtn(){
// 拿到graph就可以用官网提供的一些操作元素的方法了,还有整体渲染的各种方法,可以自己尝试一下
console.log(this.canvasGraph)
},
initG6(data) {
// 实例化g6
const graph = new G6.TreeGraph({
container: 'container',
width,
height,
fitView: true,
fitCenter: true,
maxZoom: 1.1,
minZoom: 0.5,
linkCenter: false,
// renderer: 'svg',
fitViewPadding: 5,
modes: {
default: []
},
defaultNode: {
anchorPoints: [
[0, 0.5],
[1, 0.5]
]
},
defaultEdge: {},
layout: {}
})
// 操作节点
graph.node((node) => {})
// 这里从data中自定义一个属性,在渲染之前把graph直接赋值给它
this.canvasGraph = graph
graph.data(data)
grapg.render()
}
}
九、官方提供的一些常用的方法
1、当节点中的文本太长超出节点时,一开始我是自己写了个函数,来实现文本的换行,后来发现文档中有处理文本太长的示例。传入的参数str—文本,maxWidth—节点盒子宽度,fontSize—文本尺寸。
一般配合鼠标指向弹出层使用——Tooltip,这也是官方提供的一个插件。例如还有Legend图例插件,Grid在画布绘制网格,Menu配置节点上的右击菜单,ToolBar,Fisheye鱼眼放大镜···
// 处理超长文本的函数 可根据自己的需求进行加工处理~
function labelLength (str, maxWidth, fontSize) {
const ellipsis = '...'
const ellipsisLength = G6.Util.getTextSize(ellipsis, fontSize)[0]
let currentWidth = 0
let res = str
const pattern = new RegExp('[\u4E00-\u9FA5]+')
str.split('').forEach((letter, i) => {
if (currentWidth > maxWidth - ellipsisLength) return
if (pattern.test(letter)) {
currentWidth += fontSize
} else {
currentWidth += G6.Util.getLetterWidth(letter, fontSize)
}
if (currentWidth > maxWidth - ellipsisLength) {
res = `${str.substr(0, i)}${ellipsis}`
}
})
return res
}
// <1> 在实例对象的直接使用,内置的Behavior
const graph = new G6.Graph({
container: 'mountNode',
width: 500,
height: 500,
modes: {
default: [
{
// 节点文本提示
type: 'tooltip',
// 格式化函数,可以返回文本或者 HTML
formatText(model) {
return model.label;
},
// tooltip 距离鼠标的偏移量
offset: 10,
// 是否允许 toolip 出现,此处可以判断父节点不出现,只让子节点出现
shouldBegin(e) {},
// 是否允许 toolip 内容更新
shouldUpdate(e) {}
},
],
},
});
// <2> 自定义Tooltip
const tooltip = new G6.Tooltip({
offsetX: 10,
offsetY: 20,
getContent(e) {
const outDiv = document.createElement('div');
outDiv.style.width = '180px';
outDiv.innerHTML = `
<h4>自定义tooltip</h4>
<ul>
<li>Label: ${e.item.getModel().label || e.item.getModel().id}</li>
</ul>`
return outDiv
},
itemTypes: ['node']
});
const graph = new G6.Graph({
//... 其他配置项
container: 'mountNode',
width: 500,
height: 500,
plugins: [tooltip], // 配置 Tooltip 插件
});
自定义Tooltip的配置项:
2、再带图片下载的方法,直接通过实例对象来实现canvas的下载。
也可将画布上的元素自动生成url然后实现下载~
3、通过不同文字的长短,自动适配节点盒子的宽度。(G6.Util.getTextSize这个方法会自动根据文字长短计算出需要节点的宽度和高度)
根据字体大小获取单个字母的宽度。(G6.Util.getLetterWidth这个方法会根据字体大小计算出单个字母的宽度 )
// str是节点文字,fontSize是节点字体大小
let width = G6.Util.getTextSize(str, fontSize)[0]
// 根据字体大小获取单个字母的宽度
let oneStrWidth = G6.Util.getLetterWidth(str, fontSize)
总结
今天就介绍这些了,还有好多坑,以后遇到了会继续补充。本人也是第一次用antvG6这个组件库,也是把自己的一点点微不足道的认识写一下,但是还有很多需要尝试的,欢迎大佬们也留下自己的看法。
持续更新中...