简介
基础概念
画布Graph
在x6中,Graph是图的载体,它包含图上的所有元素(节点、边等),同时挂载了图的相关操作(如交互监听、元素操作等)。
const graph = new Graph({
panning:true //支持平移
selecting: {
enabled: true, //支持选中
multiple: true,
movable: true, //支持拖动
}
}
graph.zoom(0.2) //在原来缩放级别上增加0.2
graph.zoom(-0.2) //在原来缩放级别上减少0.2
基类Cell
基类Cell定义了节点和边共同属性和方法,如属性样式、可见性、业务数据。
节点Node
node是所有节点的基类,继承自Cell,并定义了节点的通用属性和方法。
边Edge
node是所有节点的基类,继承自Cell,并定义了节点的通用属性和方法。
使用
定义画布属性
export const garghProperty = {
height: 600,
grid: true,
selecting: {
enabled: true,
multiple: true,
// movable: true,
// showNodeSelectionBox: true,
},
background: {
color: '#f8f8f8', // 设置画布背景颜色
},
snapline: {
enabled: true,
sharp: true,
},
scroller: {
enabled: true,
pannable: true,
pageVisible: false,
pageBreak: false,
},
mousewheel: {
enabled: true,
},
interacting: false,
}
定义节点属性
export const commonNode = {
width: 180,
height: 90,
data: {
fileList: [],
tool: ''
},
markup: [
{
tagName: 'rect',
attrs: {
class: 'card',
},
},
{
tagName: 'text',
attrs: {
class: 'content',
},
},
{
tagName: 'g',
attrs: {
class: 'btn add',
},
children: [
{
tagName: 'rect',
attrs: {
class: 'add',
},
},
{
tagName: 'circle',
attrs: {
class: 'add',
},
},
{
tagName: 'text',
attrs: {
class: 'add',
},
},
],
},
{
tagName: 'g',
attrs: {
class: 'btn del',
},
children: [
{
tagName: 'circle',
attrs: {
class: 'del',
},
},
{
tagName: 'text',
attrs: {
class: 'del',
},
},
],
},
],
attrs: {
'.card': {
rx: 10,
ry: 10,
refWidth: '100%',
refHeight: '100%',
fill: '#EFF4FF',
stroke: '#5F95FF',
strokeWidth: 1,
pointerEvents: 'visiblePainted',
},
'.content': {
refX: 0.05,
refY: 0.1,
fill: '#262626',
fontFamily: 'Arial',
fontSize: 14,
fontWeight: '600',
lineHeight: 20,
textWrap: {
width: 169,
height: 79,
ellipsis: true,
},
},
'.btn.add': {
refX: '32%',
refY: '95%',
refY2: 15,
event: 'node:add',
},
'.btn.del': {
refX: '100%',
event: 'node:delete',
},
'.btn.add > rect': {
rx: 10,
ry: 10,
refX: '-6%',
refY: '-10%',
width: '85px',
height: '22px',
fill: '#EFF4FF',
},
'.btn.add > circle': {
r: 7,
refX: '-0.5%',
refY: '2%',
fill: 'transparent',
stroke: '#5F95FF',
strokeWidth: 2,
},
'.btn.add > text': {
fontSize: 16,
fontWeight: 800,
fill: '#5F95FF',
x: -5.5,
y: 7,
fontFamily: 'Times New Roman',
text: '+ 新增选项',
},
'.btn.del > circle': {
r: 10,
fill: '#fff',
stroke: '#F56C6C',
strokeWidth: 2,
},
'.btn.del > text': {
fontSize: 28,
fontWeight: 500,
fill: '#F56C6C',
x: -4.5,
y: 6,
fontFamily: 'Times New Roman',
text: '-',
},
},
}
定义边属性
export const edge = {
zIndex: -1,
attrs: {
line: {
strokeWidth: 2,
stroke: '#A2B1C3',
sourceMarker: null,
},
},
}
初始化画布
//普通节点
Graph.registerNode(
'common-node',
{...commonNode},
true
)
// 自定义边
Graph.registerEdge(
'org-edge',
{...edge},
true,
)
const graph = new Graph(
{
container: document.getElementById('container'),
...garghProperty
}
);
画布事件监听
graph.on('node:add', ({e, node}) => {
e.stopPropagation()
const member = this.createNode(
'新建节点',
)
graph.freeze()
graph.addCell([member, this.createEdge(node, member)])
graph.resetSelection(member)
this.layout()
})
graph.on('node:delete', ({e, node}) => {
e.stopPropagation()
graph.freeze()
graph.removeCell(node)
this.layout()
})
graph.on('node:selected', ({node}) => {
const data = node.getData()
const form = {
id: node.id,
content: node.attrs['.content'].text,
...data
}
this.$emit('set-nodeForm', form)
});
graph.on('node:unselected', ({node}) => {
this.$emit('set-nodeForm', {})
});
// 当 cell 为节点时触发
graph.on('node:change:children', ({node}) => {
if (node.getChildCount() !== 0) {
node.setAttrs({
'.btn.del > circle': {
r: 0,
},
'.btn.del > text': {
text: '',
},
})
} else if (node.getParent() !== null) {
node.setAttrs({
'.btn.del > circle': {
r: 10,
},
'.btn.del > text': {
text: '-',
},
})
}
})
设置节点布局
//设置节点布局
layout() {
const graph = this.graph;
const dir = 'TB';
const nodes = graph.getNodes()
const edges = graph.getEdges()
const g = new dagre.graphlib.Graph()
g.setGraph({rankdir: dir, nodesep: 16, ranksep: 50})
g.setDefaultEdgeLabel(() => ({}))
const width = 260
const height = 90
nodes.forEach((node) => {
g.setNode(node.id, {width, height})
})
edges.forEach((edge) => {
const source = edge.getSource()
const target = edge.getTarget()
g.setEdge(source.cell, target.cell)
//新建节点时设置节点的父子关系
const sourceNode = graph.getCellById(source.cell);
const targetNode = graph.getCellById(target.cell);
sourceNode.addChild(targetNode);
})
dagre.layout(g)
graph.freeze()
g.nodes().forEach((id) => {
const node = graph.getCellById(id)
node.setZIndex(100)
if (node) {
const pos = g.node(id)
node.position(pos.x, pos.y)
}
})
edges.forEach((edge) => {
const source = edge.getSourceNode()
const target = edge.getTargetNode()
const sourceBBox = source.getBBox()
const targetBBox = target.getBBox()
console.log(sourceBBox, targetBBox)
if ((dir === 'LR' || dir === 'RL') && sourceBBox.y !== targetBBox.y) {
const gap =
dir === 'LR'
? targetBBox.x - sourceBBox.x - sourceBBox.width
: -sourceBBox.x + targetBBox.x + targetBBox.width
const fix = dir === 'LR' ? sourceBBox.width : 0
const x = sourceBBox.x + fix + gap / 2
edge.setVertices([
{x, y: sourceBBox.center.y},
{x, y: targetBBox.center.y},
])
} else if (
(dir === 'TB' || dir === 'BT') &&
sourceBBox.x !== targetBBox.x
) {
const gap =
dir === 'TB'
? targetBBox.y - sourceBBox.y - sourceBBox.height
: -sourceBBox.y + targetBBox.y + targetBBox.height
const fix = dir === 'TB' ? sourceBBox.height : 0
const y = sourceBBox.y + fix + gap / 2
edge.setVertices([
{x: sourceBBox.center.x + 2, y},
{x: targetBBox.center.x + 2, y},
])
} else {
edge.setVertices([])
}
})
graph.unfreeze()
},