AntV X6 技术分享
[TOC]
一、本文目的
对 AntV x6 知识点的一个总结,也为其他开发同学提供一个说明文档。
二、知识要点
1、简介
X6 是 AntV 旗下的图编辑引擎,提供了一系列开箱即用的交互组件和简单易用的节点定制能力,方便我们快速搭建流程图、DAG 图、ER 图等图应用。
2、安装
npm i @antv/x6 -S
yarn add @antv/6
3、实例代码
import { Graph } from '@antv/x6';
const graph = new Graph({
container: document.getElementById('container'),
width: 800,
height: 600,
});
4、画布 Graph
在 X6 中,Graph 是图的载体,它包含了图上的所有元素(节点、边等),同时挂载了图的相关操作(如交互监听、元素操作、渲染等)。
graph.dispose() // 销毁画布
graph.centerContent() //内容居中,画布内容中心与视口中心对齐
graph.zoom() // 获取缩放级别
graph.zoom(0.2) // 在原来缩放级别上增加 0.2
graph.zoom(-0.2) // 在原来缩放级别上减少 0.2
graph.getCells() // 返回画布中所有节点和边
graph.getNodes() // 返回画布中所有节点
graph.getEdges() // 返回画布中所有边
graph.toJSON() // 导出图中的节点和边
graph.fromJSON() // 反序列化 按照指定的 JSON 数据渲染节点和边。
graph.clearCells() // 清空画布
graph.resetCells() // 清空画布并添加用指定的节点/边
graph.getCellById() 根据节点/边的 ID 获取节点/边。
b. 画布反序列化
const data = {
// 节点
nodes: [
{
id: 'node1',
x: 40,
y: 40,
width: 80,
height: 40,
label: 'Hello'
},
{
id: 'node2',
x: 160,
y: 180,
width: 80,
height: 40,
label: 'World'
}
],
// 边
edges: [
{
source: 'node1',
target: 'node2'
}
]
}
graph.fromJSON(data)
5、节点 Node
Node 是所有节点的基类,继承自 Cell,并定义了节点的通用属性和方法。
a. 常用方法:
node.isNode() // 判断是不是节点
node.getBBox() // 获取节点的包围盒
node.size() // 获取节点大小
node.resize() // 改变节点大小
node.scale() // 缩放节点
node.position() // 获取节点位置
node.position(30, 30) // 设置节点位置
b. 创建内置节点
// 方式一:构造函数
import { Shape } from '@antv/x6'
// 创建节点
const rect = new Shape.Rect({
x: 100,
y: 200,
width: 80,
height: 40,
angle: 30,
attrs: {
body: {
fill: 'blue',
},
label: {
text: 'Hello',
fill: 'white',
},
},
})
graph.addNode(rect)
// 方式二:graph.addNode
const rect = graph.addNode({
shape: 'rect', // 指定使用何种图形,默认值为 'rect'
x: 100,
y: 200,
width: 80,
height: 40,
angle: 30,
attrs: {
body: {
fill: 'blue',
},
label: {
text: 'Hello',
fill: 'white',
},
},
})
// 方式三:graph.createNode
const node = graph.createNode({
width: 75,
height: 38,
shape: 'html',
attrs: {
label: {
text: '开始', // 文本
style: {
visibility: 'hidden'
}
}
},
ports: {
groups,
items: startGroupItems
},
type: 'start',
nodeDescription: '开始节点描述',
html: 'start-html',
...startNodeSetting
});
this.dnd.start(node, e);
c. 注册自定义 HTML 节点
registerHTMLComponent() { npm install @antv/x6-vue-shape import { Graph } from '@antv/x6' Graph.registerVueComponent( const graph = new Graph({ const node1 = graph.addNode({ const node2 = graph.addNode({ graph.addEdge({ Edge 是边的基类,继承自 Cell,并定义了边的通用属性和方法。 source: { x: 40, y: 40 }, const edge = new Shape.Edge({ const edge = new Shape.Edge({ const edge = new Shape.Edge({ import { Shape } from '@antv/x6'; export class TreeEdge extends Shape.Edge { Edge.registry.register('tree-edge', TreeEdge, true); edge.isEdge() // 判断是不是边 // 内置箭头:https://x6.antv.vision/zh/docs/tutorial/intermediate/marker#%E5%86%85%E7%BD%AE%E7%AE%AD%E5%A4%B4 const markers = [ markers.forEach(([marker, args], i) => { Cell 是 Node 和 Edge 的基类,包含节点和边的通用属性和方法定义,如属性样式、可见性、业务数据等,并且在实例化、样式定制、默认选项、自定义选项等方面具有相同的行为。 cell.isNode() // 监测是否是Node实例 import { ToolsView, Graph } from '@antv/x6'; insertCss( export class TooltipTool extends ToolsView.ToolItem { this.knob = ToolsView.createElement('div', false); return this; updatePosition() { if (description && name) { this.knob.querySelector('.inner-box-main-title').innerHTML = name; TooltipTool.config({ Graph.registerNodeTool('tooltip', TooltipTool, true); // 页面实例化
const startHtml =
;
const approvalHtml =
const aggregationHtml =
;
const endHtml =
;
const branchHtml =
;
Graph.registerHTMLComponent('start-html', startHtml, true);
Graph.registerHTMLComponent('approval-html', approvalHtml, true);
Graph.registerHTMLComponent('aggregation-html', aggregationHtml, true);
Graph.registerHTMLComponent('end-html', endHtml, true);
Graph.registerHTMLComponent('branch-html', branchHtml, true);
}
d. 渲染 Vue 节点
在 vue2 下还需要安装 @vue/composition-api
import '@antv/x6-vue-shape'
import ServiceNode from './service-node.vue'
'service-node',
{
template: '
components: {
ServiceNode
}
},
true
)
container: document.getElementById('graph')
})
shape: "vue-shape",
x: 300,
y: 250,
component: "service-node"
});
shape: "vue-shape",
x: 300,
y: 550,
component: "service-node"
});
source: node1,
target: node2,
vertices: [
{ x: 300, y: 250 },
{ x: 300, y: 550 }
]
});
6、边 Edge
a. 连接到画布上的点const edge = new Shape.Edge({
target: { x: 180, y: 80 },
}) b. 连接到节点/边
source: { cell: 'source-cell-id' },
target: { cell: 'target-cell-id' },
}) c. 连接到节点上的链接桩
source: { cell: 'source-cell-id', port: 'port-id' },
target: { cell: 'target-cell-id', port: 'port-id' },
}) d. 连接到节点上的某个元素
source: { cell: 'source-cell-id', selector: 'some-selector' },
target: { cell: 'target-cell-id', selector: 'some-selector' },
}) e. 自定义边:
// ...
}
TreeEdge.config({
zIndex: 1
}); f. 常用的方法有
edge.getBBox() // 返回边的包围盒
edge.getSource() // 获取边的起始节点/起始点信息
edge.getTarget() // 获取边的终止节点/终止点信息 g. 箭头
['block', { size: 6 }],
['classic', { size: 6 }],
['diamond', { size: 8 }],
['circle', { size: 6 }],
['circlePlus', { size: 6 }],
['ellipse', { rx: 6, ry: 4 }],
['cross', { size: 8, offset: 1 }],
['async', { size: 8, offset: 1 }],
]
graph.addEdge({
sourcePoint: [220, 30 + i * 40],
targetPoint: [500, 30 + i * 40],
label: marker,
attrs: {
line: {
sourceMarker: {
args,
name: marker,
},
targetMarker: {
args,
name: marker,
},
strokeWidth: 1,
},
},
})
}) 7、基类 cell
a. 常用的方法有:
cell.isEdge() // 监测是否是Edge实例
cell.attr() // 获取全部属性值
cell.attr('body/fill') // 获取某一属性值
cell.attr('body/fill', '#f5f5f5') // 设置某一属性值
cell.getProp().type // 获取指定的属性值。
cell.setProp('name', val); // 设置属性
cell.removeProp('zIndex'); // 删除单个属性 8、辅助工具
a. 自定义 tooltip 弹框
import insertCss from 'insert-css';.inner-box { box-sizing: border-box; width: 590px; z-index: 2; max-height: 175px; padding: 20px; color: rgba(255, 255, 255, 65); background-color: rgba(36, 40, 52, 1); box-shadow: 0px 2px 12px 0px rgba(0, 0, 0, 0.5); border: 1px solid rgba(255, 255, 255, 0.2); position: absolute; display: none; } .inner-box::before { width: 0; height: 0; content: ''; border-left: 7px solid transparent; border-right: 7px solid transparent; border-bottom: 10px solid rgba(255, 255, 255, 0.2); position: absolute; top: -10px; left: 49px; } .inner-box::after { width: 0; height: 0; content: ''; border-left: 6px solid transparent; border-right: 6px solid transparent; border-bottom: 8px solid rgba(36, 40, 52, 1); position: absolute; top: -8px; left: 50px; } .inner-box-main-title { font-size: 18px; font-weight: 600; color: rgba(65, 133, 255, 100); } .inner-box-main-text { font-size: 12px; line-height: 20px; }
);
render() {
const dom =
;
this.knob.setAttribute('class', 'inner-box');
this.knob.innerHTML = dom;
this.container.appendChild(this.knob);
this.updatePosition();
}
const cell = this.cell;
const { position, name, description } = cell.getProp();
const style = this.knob.style;
style.display = 'block';
style.left = ${position.x}px
;
style.top = ${position.y + 70}px
;
this.knob.querySelector('.inner-box-main-text').innerHTML = description;
}
}
}
tagName: 'div',
isSVGElement: false
});
new TooltipTool()
cell.addTools([{ name: 'tooltip' }]); // 添加tooltip
cell.removeTools(); // 删除tooltip