This commit is contained in:
asp_ly 2024-12-13 20:59:39 +08:00
parent 8186bdfba5
commit 4889312eb3

View File

@ -2,6 +2,7 @@ import { Graph } from '@antv/x6';
import dagre from 'dagre'; import dagre from 'dagre';
interface NodeData { interface NodeData {
id: string;
graph: { graph: {
position?: { position?: {
x: number; x: number;
@ -10,17 +11,24 @@ interface NodeData {
}; };
} }
interface RelativePosition {
id: string;
position: {
x: number;
y: number;
};
}
/** /**
* *
* @param nodes * @param nodes
* @returns * @returns
*/ */
const hasAllNodesPosition = (nodes: NodeData[]) => { const hasAllNodesPosition = (nodes: NodeData[]): boolean => {
return nodes.every(node => { return nodes.every(node => {
const graphData = node.graph; const graphData = node.graph;
return graphData && graphData.position && return graphData?.position?.x != null &&
typeof graphData.position.x === 'number' && graphData?.position?.y != null;
typeof graphData.position.y === 'number';
}); });
}; };
@ -28,29 +36,83 @@ const hasAllNodesPosition = (nodes: NodeData[]) => {
* *
* @param graph * @param graph
*/ */
const centerAndFit = (graph: Graph) => { const centerAndFit = (graph: Graph): void => {
const nodes = graph.getNodes();
if (nodes.length === 0) return;
// 计算所有节点的边界
let minX = Infinity;
let minY = Infinity;
let maxX = -Infinity;
let maxY = -Infinity;
nodes.forEach(node => {
const position = node.getPosition();
const size = node.getSize();
minX = Math.min(minX, position.x);
minY = Math.min(minY, position.y);
maxX = Math.max(maxX, position.x + size.width);
maxY = Math.max(maxY, position.y + size.height);
});
// 获取画布大小 // 获取画布大小
const container = graph.container; const container = graph.container;
if (!container) return; if (!container) return;
const containerBBox = container.getBoundingClientRect(); const containerBBox = container.getBoundingClientRect();
const padding = 40; // 设置更大的内边距 const padding = 40;
// 居中并缩放 // 计算图形的宽度和高度
graph.centerContent(); const graphWidth = maxX - minX;
graph.zoomToFit({ const graphHeight = maxY - minY;
padding,
maxScale: 1, // 限制最大缩放比例 // 计算缩放比例
minScale: 0.5, // 限制最小缩放比例 const scaleX = (containerBBox.width - padding * 2) / graphWidth;
const scaleY = (containerBBox.height - padding * 2) / graphHeight;
const scale = Math.min(Math.min(scaleX, scaleY), 1);
// 计算居中位置
const tx = (containerBBox.width - graphWidth * scale) / 2 - minX * scale;
const ty = (containerBBox.height - graphHeight * scale) / 2 - minY * scale;
// 应用变换
graph.scale(scale);
graph.translate(tx, ty);
};
/**
*
* @param nodes
* @returns
*/
const calculateRelativePositions = (nodes: NodeData[]): RelativePosition[] | null => {
if (nodes.length === 0) return null;
// 计算所有节点的最小坐标
let minX = Infinity;
let minY = Infinity;
nodes.forEach(node => {
if (node.graph?.position) {
minX = Math.min(minX, node.graph.position.x);
minY = Math.min(minY, node.graph.position.y);
}
}); });
// 确保在视口中心 // 计算相对位置
const graphBBox = graph.getBBox(); const positions = nodes.map(node => {
if (graphBBox) { if (node.graph?.position) {
const tx = (containerBBox.width - graphBBox.width) / 2; return {
const ty = (containerBBox.height - graphBBox.height) / 2; id: node.id,
graph.translate(tx, ty); position: {
x: node.graph.position.x - minX,
y: node.graph.position.y - minY
} }
};
}
return null;
}).filter((pos): pos is RelativePosition => pos !== null);
return positions.length > 0 ? positions : null;
}; };
/** /**
@ -58,23 +120,32 @@ const centerAndFit = (graph: Graph) => {
* @param graph * @param graph
* @param nodes * @param nodes
*/ */
export const applyAutoLayout = (graph: Graph, nodes: NodeData[] = []) => { export const applyAutoLayout = (graph: Graph, nodes: NodeData[] = []): void => {
// 如果所有节点都有位置信息,只进行居中显示 // 如果所有节点都有位置信息,计算并应用相对位置
if (nodes.length > 0 && hasAllNodesPosition(nodes)) { if (nodes.length > 0 && hasAllNodesPosition(nodes)) {
// 等待所有节点渲染完成后再居中 const relativePositions = calculateRelativePositions(nodes);
setTimeout(() => { if (relativePositions) {
// 应用相对位置
graph.getNodes().forEach(node => {
const nodePosition = relativePositions.find(pos => pos.id === node.id);
if (nodePosition) {
node.position(nodePosition.position.x, nodePosition.position.y);
}
});
// 立即居中显示
centerAndFit(graph); centerAndFit(graph);
}, 100); }
return; return;
} }
// 创建布局图
const g = new dagre.graphlib.Graph(); const g = new dagre.graphlib.Graph();
g.setGraph({ g.setGraph({
rankdir: 'LR', rankdir: 'LR',
align: 'UL', align: 'UL',
ranksep: 100, // 增加节点间距 ranksep: 100,
nodesep: 60, // 增加节点间距 nodesep: 60,
marginx: 40, // 增加边距 marginx: 40,
marginy: 40, marginy: 40,
}); });
g.setDefaultEdgeLabel(() => ({})); g.setDefaultEdgeLabel(() => ({}));
@ -108,6 +179,6 @@ export const applyAutoLayout = (graph: Graph, nodes: NodeData[] = []) => {
} }
}); });
// 居中显示并适应画布 // 立即居中显示
centerAndFit(graph); centerAndFit(graph);
}; };