前几天接到了个需求,做一个树形的流程图,大概形状类似于antV-G6里面的自定义树图
antv-G6自定义树图链接:定制树图元素样式 | G6
但是需求:父节点和子节点能够展开和关闭,还要有这个功能
emmm..对于每接触过G6的小白来说...真的难哭了
我在网上一顿找..发现并没有合适的可以直接用的..只能自己去改造,但是改造的前提的我得先学G6,所以这篇是我对G6给的树图的学习--大家可以参考
首先肯定是要下载G6了--官网
npm install --save @antv/g6
注意:下载之后启动,如果报错core.js的问题,可以下载一下他的相对最新版
npm install [email protected]
下面就是对G6各个配置项的学习注释了
import G6 from '@antv/g6'
// 折叠图标函数
const COLLAPSE_ICON = function COLLAPSE_ICON (x, y, r) {
return [
['M', x - r, y - r],
['a', r, r, 0, 1, 0, r * 2, 0],
['a', r, r, 0, 1, 0, -r * 2, 0],
['M', x + 2 - r, y - r],
['L', x + r - 2, y - r]
]
}
// 展开图标函数
const EXPAND_ICON = function EXPAND_ICON (x, y, r) {
return [
['M', x - r, y - r],
['a', r, r, 0, 1, 0, r * 2, 0],
['a', r, r, 0, 1, 0, -r * 2, 0],
['M', x + 2 - r, y - r],
['L', x + r - 2, y - r],
['M', x, y - 2 * r + 2],
['L', x, y - 2]
]
}
// 树形图数据
const data = {
id: 'root',
label: 'root',
children: [
{
id: 'c1',
label: 'c1',
children: [
{
id: 'c1-1',
label: 'c1-1'
},
{
id: 'c1-2',
label: 'c1-2',
children: [
{
id: 'c1-2-1',
label: 'c1-2-1'
},
{
id: 'c1-2-2',
label: 'c1-2-2'
}
]
}
]
},
{
id: 'c2',
label: 'c2'
},
{
id: 'c3',
label: 'c3',
children: [
{
id: 'c3-1',
label: 'c3-1'
},
{
id: 'c3-2',
label: 'c3-2',
children: [
{
id: 'c3-2-1',
label: 'c3-2-1'
},
{
id: 'c3-2-2',
label: 'c3-2-2'
},
{
id: 'c3-2-3',
label: 'c3-2-3'
}
]
},
{
id: 'c3-3',
label: 'c3-3'
}
]
}
]
}
// 深度优先遍历树数据 从根到子
G6.Util.traverseTree(data, (d) => {
d.leftIcon = {
style: {
fill: '#e6fffb',
stroke: '#e6fffb'
},
img: 'https://gw.alipayobjects.com/mdn/rms_f8c6a0/afts/img/A*Q_FQT6nwEC8AAAAAAAAAAABkARQnAQ'
}
return true
})
// 自定义元素 G6.registerX
// 自定义节点-当内置节点不满足需求时,可以通过 G6.registerNode(nodeName, options, extendedNodeName) 方法自定义节点。
// nodeName 自定义节点的名称 唯一性 必须
// options,自定义节点的配置项是个对象 必须
// extendedNodeName 可以基于内置节点自定义节点
G6.registerNode(
'icon-node',
{
options: {
size: [60, 20], // 宽高
stroke: '#91d5ff', // 变颜色
fill: '#91d5ff'// 填充色
},
// draw是绘制后的附加操作-节点的配置项 图形分组,节点中图形对象的容器
draw (cfg, group) {
// 获取节点的配置
const styles = this.getShapeStyle(cfg)
// 解构赋值
const { labelCfg = {} } = cfg
const w = styles.width
const h = styles.height
// 向分组中添加新的图形 图形 配置 rect矩形 xy 代表左上角坐标 w h是宽高
const keyShape = group.addShape('rect', {
attrs: {
...styles,
x: -w / 2,
y: -h / 2
}
})
console.log('cfg.leftIcon', cfg.leftIcon)
// 如果有左侧图标 向分组中添加新的图形的配置
if (cfg.leftIcon) {
const { style, img } = cfg.leftIcon
group.addShape('rect', {
attrs: {
x: 1 - w / 2,
y: 1 - h / 2,
width: 38,
height: styles.height - 2,
fill: '#8c8c8c',
...style
}
})
group.addShape('image', {
attrs: {
x: 8 - w / 2,
y: 8 - h / 2,
width: 24,
height: 24,
img:
img ||
'https://g.alicdn.com/cm-design/arms-trace/1.0.155/styles/armsTrace/images/TAIR.png'
},
name: 'image-shape'
})
}
// 如果不需要动态增加或删除元素,则不需要 add 这两个 marker
group.addShape('marker', {
attrs: {
x: 40 - w / 2,
y: 52 - h / 2,
r: 6,
stroke: '#73d13d',
cursor: 'pointer',
symbol: EXPAND_ICON
},
name: 'add-item'
})
group.addShape('marker', {
attrs: {
x: 80 - w / 2,
y: 52 - h / 2,
r: 6,
stroke: '#ff4d4f',
cursor: 'pointer',
symbol: COLLAPSE_ICON
},
name: 'remove-item'
})
// 文本文字的配置
if (cfg.label) {
group.addShape('text', {
attrs: {
...labelCfg.style,
text: cfg.label,
x: 50 - w / 2,
y: 25 - h / 2
}
})
}
return keyShape
},
// 更新节点后的操作,一般同 afterDraw 配合使用
update: undefined
},
'rect'
)
// 自定义边
// 参数1 自定义边的名称 参数2 自定义边时的配置项(对象) 参数3 自定义边时可基于内置边进行定义(选填)
G6.registerEdge('flow-line', {
// 绘制后的附加操作
draw (cfg, group) {
// 边两端与起始节点和结束节点的交点;
const startPoint = cfg.startPoint
const endPoint = cfg.endPoint
// 边的配置
const { style } = cfg
const shape = group.addShape('path', {
attrs: {
stroke: style.stroke, // 边框的样式
endArrow: style.endArrow, // 结束箭头
// 路径
path: [
['M', startPoint.x, startPoint.y],
['L', startPoint.x, (startPoint.y + endPoint.y) / 2],
['L', endPoint.x, (startPoint.y + endPoint.y) / 2],
['L', endPoint.x, endPoint.y]
]
}
})
return shape
}
})
// 默认的鼠标悬停会加粗,边框颜色改变
const defaultStateStyles = {
hover: {
stroke: '#1890ff',
lineWidth: 2
}
}
// 默认节点的颜色 边 圆角的配置
const defaultNodeStyle = {
fill: '#91d5ff',
stroke: '#40a9ff',
radius: 5
}
// 默认边的颜色 末尾箭头
const defaultEdgeStyle = {
stroke: '#91d5ff',
endArrow: {
path: 'M 0,0 L 12, 6 L 9,0 L 12, -6 Z',
fill: '#91d5ff',
d: -20
}
}
// 默认布局
// compactBox 紧凑树布局
// 从根节点开始,同一深度的节点在同一层,并且布局时会将节点大小考虑进去。
const defaultLayout = {
type: 'compactBox', // 布局类型树
direction: 'TB', // TB 根节点在上,往下布局
getId: function getId (d) { // 节点 id 的回调函数
return d.id
},
getHeight: function getHeight () { // 节点高度的回调函数
return 16
},
getWidth: function getWidth () { // 节点宽度的回调函数
return 16
},
getVGap: function getVGap () { // 节点纵向间距的回调函数
return 40
},
getHGap: function getHGap () { // 节点横向间距的回调函数
return 70
}
}
// 文本配置项
const defaultLabelCfg = {
style: {
fill: '#000',
fontSize: 12
}
}
// 获取容器
const container = document.getElementById('container')
// 获取容器的宽高
const width = container.scrollWidth
const height = container.scrollHeight || 500
// 实例化 Grid 插件-小地图
const minimap = new G6.Minimap({
size: [150, 100]
})
// 实例化 Menu 插件-右击
const menu = new G6.Menu({
offsetX: 6,
offsetY: 10,
itemTypes: ['node'],
getContent (e) {
const outDiv = document.createElement('div')
outDiv.style.width = '180px'
outDiv.innerHTML = `
- 测试01
- 测试01
- 测试01
- 测试01
- 测试01
`
return outDiv
}
})
// Graph 是 G6 图表的载体-实例化
const graph = new G6.TreeGraph({
container: 'container', // 图的 DOM 容器
width,
height,
linkCenter: true, // 指定边是否连入节点的中心
plugins: [minimap, menu], // 插件
modes: { // 交互模式
// default 模式中包含点击选中节点行为和拖拽画布行为;
default: ['drag-canvas', 'zoom-canvas']
},
// 默认状态下节点的配置
defaultNode: {
type: 'icon-node',
size: [120, 40],
style: defaultNodeStyle,
labelCfg: defaultLabelCfg
},
// 默认状态下边的配置,
defaultEdge: {
type: 'flow-line',
style: defaultEdgeStyle
},
// 各个状态下节点的样式-,例如 hover、selected,3.1 版本新增。
nodeStateStyles: defaultStateStyles,
// 各个状态下边的样式-,例如 hover、selected,3.1 版本新增。
edgeStateStyles: defaultStateStyles,
// 布局配置项
layout: defaultLayout
})
// 初始化的图数据,是一个包括 nodes 数组和 edges 数组的对象
graph.data(data)
// 根据提供的数据渲染视图。
graph.render()
// 让画布内容适应视口。
graph.fitView()
// 改变视口的缩放比例,在当前画布比例下缩放,是相对比例。
graph.zoom(1)
// 鼠标移入元素范围内触发
graph.on('node:mouseenter', (evt) => {
const { item } = evt
graph.setItemState(item, 'hover', true)
})
// 鼠标移出目标元素后触发
graph.on('node:mouseleave', (evt) => {
const { item } = evt// item表示触发元素
graph.setItemState(item, 'hover', false)
})
// 单击鼠标左键或者按下回车键时触发
graph.on('node:click', (evt) => {
// item: 事件的触发元素(节点/边/ Combo)
// target: 事件的触发图形 Shape 或画布对象
const { item, target } = evt
// type: 事件类型
// name: 事件名称
const targetType = target.get('type')
const name = target.get('name')
// 增加元素
if (targetType === 'marker') {
const model = item.getModel()
if (name === 'add-item') {
if (!model.children) {
model.children = []
}
const id = `n-${Math.random()}`
model.children.push({
id,
label: id.substr(0, 8),
leftIcon: {
style: {
fill: '#e6fffb',
stroke: '#e6fffb'
},
img:
'https://gw.alipayobjects.com/mdn/rms_f8c6a0/afts/img/A*Q_FQT6nwEC8AAAAAAAAAAABkARQnAQ'
}
})
graph.updateChild(model, model.id)
} else if (name === 'remove-item') {
// // 移除节点
graph.removeChild(model.id)
}
}
})
if (typeof window !== 'undefined') {
window.onresize = () => {
if (!graph || graph.get('destroyed')) return
if (!container || !container.scrollWidth || !container.scrollHeight) return
graph.changeSize(container.scrollWidth, container.scrollHeight)
}
}
下面的是我根据G6树图的改造的 (在vue组件中写的)--效果图
数据用的假数据 treedata.js
// 属性图的数据
export const treedata = {
id: 'root',
label: '根',
children: [
{
id: '1',
label: '一级',
children: [
{
id: '1-1',
label: '二级'
},
{
id: '1-2',
label: '二级',
children: [
{
id: '1-2-1',
label: '三级'
},
{
id: '1-2-2',
label: '三级'
}
]
}
]
},
{
id: '2',
label: '一级'
},
{
id: '3',
label: '一级',
children: [
{
id: '3-1',
label: '二级'
},
{
id: '3-2',
label: '二级',
children: [
{
id: '3-2-1',
label: '三级'
},
{
id: '3-2-2',
label: '三级'
},
{
id: '3-2-3',
label: '三级'
}
]
},
{
id: '3-3',
label: '二级'
}
]
}
]
}
[添加,删除,修改,查看功能还没写~慢慢来]