console
const { Graph, Cell, Node, Dom } = X6;
// 自定义节点
Graph.registerNode(
'org-node',
{
width: 72,
height: 100,
markup: [
{
tagName: 'rect',
attrs: {
class: 'card',
},
},
{
tagName: 'path',
attrs: {
class: 'image'
},
},
{
tagName: 'text',
attrs: {
class: 'rank',
},
},
{
tagName: 'text',
attrs: {
class: 'name',
},
},
{
tagName: 'text',
attrs: {
class: 'third',
},
}
],
attrs: {
'.card': {
rx: 50,
ry: 50,
refY: 4,
width: 72,
height: 72,
fill: '#1890FF',
pointerEvents: 'visiblePainted',
},
'.image': {
refX: 12,
refY: 16,
fill: '#ffffff',
event: 'node:expand'
},
'.rank': {
refX: 0.5,
refY: 95,
fill: '#000',
fontFamily: 'Courier New',
fontSize: 14,
fontWeight: '600',
textAnchor: 'middle',
textVerticalAnchor: 'middle',
},
'.name': {
refX: 0.5,
refY: 110,
fill: '#000000',
fontFamily: 'Arial',
fontSize: 12,
textAnchor: 'middle',
},
'.third': {
refX: 0.5,
refY: 130,
fill: '#333333',
fontFamily: 'Arial',
fontSize: 12,
textAnchor: 'middle',
}
},
},
true,
)
// 自定义边
Graph.registerEdge(
'org-edge',
{
zIndex: -1,
attrs: {
line: {
strokeWidth: 2,
strokeDasharray: "5, 5",
stroke: '#A2B1C3',
sourceMarker: null,
targetMarker: null,
},
},
},
true,
)
const clockIcon = 'm24,0c13.26,0,24,10.74,24,24s-10.74,24-24,24S0,37.26,0,24,10.74,0,24,0Zm-.16,9c-.54,0-.99.41-1.04.94l-1.8,17.95h0s0,0,0,0h0s0,0,0,0l13.59,4.38c.51.16,1.06-.07,1.29-.55v-.02c.24-.49.06-1.07-.39-1.36l-9.16-5.75-1.47-14.67c-.05-.53-.49-.93-1.02-.94h-.02Z';
// 布局方向
const dir = 'TB' // LR RL TB BT
// 创建画布
const graph = new Graph({
container: document.getElementById('canvasBox'),
panning: true,
interacting: false,
background: {
color: '#ffffff'
},
mousewheel: {
enabled: true,
zoomAtMousePosition: true,
minScale: 0.2,
factor: 1.1,
maxScale: 2
}
})
// 监听自定义事件
function setup() {
graph.on('node:expand', ({ e, node }) => {
e.stopPropagation()
const nodeData = node.getData();
if (nodeData.loaded) {
const expanded = nodeData.expanded;
nodeData.expanded = !nodeData.expanded;
const collapsed = !nodeData.expanded
const run = (pre) => {
const succ = graph.getSuccessors(pre, { distance: 1 })
if (succ) {
succ.forEach((node) => {
node.toggleVisible(!collapsed)
if (expanded) {
run(node)
}
})
}
}
run(node)
} else {
nodeData.expanded = true;
node.setData({
loaded: true
})
const member = createNode(
'Employee',
'New Employee',
'New Employee'
)
graph.addCell([member, createEdge(node, member)])
}
layout()
})
}
// 自动布局
function layout() {
const nodes = graph.getNodes()
const edges = graph.getEdges()
const g = new dagre.graphlib.Graph()
g.setGraph({ rankdir: dir, nodesep: 20, ranksep: 80 })
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)
})
dagre.layout(g)
g.nodes().forEach((id) => {
const node = graph.getCellById(id)
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()
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, y },
{ x: targetBBox.center.x, y },
])
} else {
edge.setVertices([])
}
})
}
function createNode(rank, name, third) {
const attrs = {
'.image': { d: clockIcon },
'.rank': {
text: Dom.breakText(rank, { width: 160, height: 45 }),
},
'.name': {
text: Dom.breakText(name, { width: 160, height: 45 }),
}
}
if (third) {
attrs['.third'] = {
text: Dom.breakText(third, { width: 160, height: 45 }),
}
}
return graph.createNode({
shape: 'org-node',
data: {
loaded: false,
expanded: false
},
attrs
})
}
function createEdge(source, target) {
return graph.createEdge({
shape: 'org-edge',
source: { cell: source.id },
target: { cell: target.id },
})
}
const nodes = [
createNode('Founder & Chairman', 'Pierre Omidyar'),
createNode('President & CEO', 'Margaret C. Whitman'),
createNode('President, PayPal', 'Scott Thompson'),
createNode('President, Ebay Global Marketplaces', 'Devin Wenig'),
createNode('Senior Vice President Human Resources', 'Jeffrey S. Skoll'),
createNode('Senior Vice President Controller', 'Steven P. Westly'),
]
const edges = [
createEdge(nodes[1], nodes[2]),
createEdge(nodes[1], nodes[3]),
createEdge(nodes[1], nodes[4]),
createEdge(nodes[1], nodes[5]),
]
graph.resetCells([...nodes, ...edges])
layout()
graph.zoomTo(0.8)
graph.centerContent()
setup()
<script src="https://cdnjs.cloudflare.com/ajax/libs/antv-x6/2.0.0/index.js"></script>
<script src="https://cdn.jsdelivr.net/npm/@antv/x6/dist/index.js"></script>
<script src="https://unpkg.com/@antv/x6/dist/index.js"></script>
<script src="https://dagrejs.github.io/project/dagre/v0.7.5/dagre.min.js"></script>
<div id="canvasBox" class="canvas-box"></div>
html,body {
padding: 0;
margin: 0;
}
.canvas-box {
height: 500px;
border: 1px solid #cccccc;
}
.canvas-box .x6-node .image {
cursor: pointer;
}