AntV X6 布局管理器深度解析:迭代布局与父子嵌套
图编辑应用的核心在于“看得清、看得准”。在多层级的概念模型中,节点大小与位置需要动态适配父子关系与连线结构。本文解析一个基于 dagre 的迭代式布局管理器,如何在 AntV X6 中实现稳定、可控的布局流程。
设计目标
- 根据层级自动设置节点大小
- 父节点自适应包裹子节点边界
- 根节点与子树分别布局,整体收敛稳定
- 提供方向与间距配置,适配不同业务场景
初始化与配置
初始化尺寸
javascript
setNodeSize() {
for (let level = 1; level <= this.options.leafLevel; level++) {
this.options.nodeSizes.push({
width: this.options.leafSize.width + this.options.embedPadding * (this.options.leafLevel - level) * 2,
height: this.options.leafSize.height + this.options.embedPadding * (this.options.leafLevel - level) * 2,
})
}
}布局主流程
入口: layout()
javascript
layout() {
const { nodes, edges } = this._getGraphElements()
if (nodes.length == 0) return
this._executeLayoutSteps(nodes, edges)
}步骤拆解:
- 节点分类: _categoryizeNodesByLevel()
- 迭代布局: _performIterativeLayout()
- 计算所有节点大小(自底向上):_calculateOptimalSizes()
- 计算所有节点位置(自顶向下):_calculateOptimalPositions()
大小计算(父子关系)
父节点大小由子节点边界与内边距计算得到:__calculateParentNodeSizes()
设置父节点尺寸:__setParentNodeSize()
javascript
_setParentNodeSize(parentNode, bounds) {
if (bounds.isValid) {
const parentWidth = bounds.maxX - bounds.minX + 2 * this.options.embedPadding
const parentHeight = bounds.maxY - bounds.minY + 2 * this.options.embedPadding
parentNode.setSize(parentWidth, parentHeight)
}
}叶子节点默认大小:_setLeafNodeSizes() (LayoutManager.js:157-164)
位置计算(根与子树)
根节点布局(使用当前实际尺寸):__layoutRootNodesWithCurrentSizes()
javascript
const rootGraph = this._createDagreGraph(1);
rootNodes.forEach((node) => {
const currentSize = node.getSize();
rootGraph.setNode(node.id, {
width: currentSize.width,
height: currentSize.height,
});
});
dagre.layout(rootGraph);
this._applyNodePositions(rootGraph);子节点相对父节点重新定位:_repositionChildrenForParent()
关键偏移量计算
javascript
const offsetX = parentPos.x + this.options.embedPadding - bounds.minX;
const offsetY = parentPos.y + this.options.embedPadding - bounds.minY;
// 根据偏移与当前大小设置最终位置
childNode.position(finalX, finalY, { skipParentHandler: true });方向与间距
布局方向由层级决定:_getDirectionForLevel()
javascript
_getDirectionForLevel(level) {
return this.options.direction[level - 1]
}- nodesep(同层间距)与 ranksep(层级间距)在 dagre.setGraph 中配置。
连线参与布局
在根与子图中,连线用于辅助 dagre 优化节点的相对位置:
- 根层连线:_addEdgesBetweenNodes()
- 子层连线:_addChildEdgesToGraph()
在某些场景可进一步使用 _adjustEdgePaths() 优化连线路径(L 型、跳线等)。
使用示例与建议
- 在 Vue 组件数据渲染后执行布局
javascript
nodeManager.createNodes(graphData.value.nodes);
edgeManager.createEdges(graphData.value.edges);
layoutManager.layout();总结
该布局管理器通过“自底向上尺寸计算 + 自顶向下位置应用”的迭代策略,解决了父子节点嵌套与 dagre 全局布局的兼容问题。结合 X6 的节点/边 API,可在复杂建模场景中保持良好的可读性与稳定性。