最近项目中需要用到拓扑图的展示,最开始选用的是antv的拓扑图组件。antv组件虽然此很方便,但是在布局的时候总是会有莫名其妙的bug,然后自己也想法去解决(看前辈经验、官方issue),最后还是不能解决。于是更换了组件库,也就是我们今天的主角:react-flow。
React Flow 是一个基于 React 的用于构建可视化流程图和图形编辑器的库。它提供了一个灵活的、可扩展的组件集合,使开发者可以轻松地创建交互式的流程图和图形编辑器应用。
tyarn add react-flow-renderer
import React, { useCallback } from "react";
import ReactFlow, { useNodesState, useEdgesState, updateEdge } from "reactflow";
import { TrialCmdWrapper } from "@/pages/trial/style";
import { FlowContent } from "./style";
import "reactflow/dist/style.css";
import { useMemo } from "react";
import CuFlowNode from "@/components/Home/resTopo/topo/node";
const initialNodes = [
{
id: "root",
type: "input",
data: { label: "全局节点" },
position: { x: 0, y: 0 }
},
{
id: "horizontal-2",
sourcePosition: "right",
targetPosition: "left",
data: { label: "A Node" },
position: { x: 250, y: 0 }
},
{
id: "horizontal-3",
sourcePosition: "right",
targetPosition: "left",
data: { label: "Node 3" },
position: { x: 250, y: 160 }
},
{
id: "horizontal-4",
sourcePosition: "right",
targetPosition: "left",
data: { label: "Node 4" },
position: { x: 500, y: 0 }
},
{
id: "horizontal-5",
sourcePosition: "top",
targetPosition: "bottom",
data: { label: "Node 5" },
position: { x: 500, y: 100 }
},
{
id: "horizontal-6",
sourcePosition: "bottom",
targetPosition: "top",
data: { label: "Node 6" },
position: { x: 500, y: 230 }
},
{
id: "horizontal-7",
sourcePosition: "right",
targetPosition: "left",
data: { label: "Node 7" },
position: { x: 750, y: 50 }
},
{
id: "horizontal-8",
sourcePosition: "right",
targetPosition: "left",
data: { label: "Node 8" },
position: { x: 750, y: 300 }
}
];
const initialEdges = [
{
id: "horizontal-e1-2",
source: "root",
type: "smoothstep",
target: "horizontal-2",
animated: true
},
{
id: "horizontal-e1-3",
source: "root",
type: "smoothstep",
target: "horizontal-3",
animated: true
},
{
id: "horizontal-e1-4",
source: "horizontal-2",
type: "smoothstep",
target: "horizontal-4",
label: "edge label"
},
{
id: "horizontal-e3-5",
source: "horizontal-3",
type: "smoothstep",
target: "horizontal-5",
animated: true
},
{
id: "horizontal-e3-6",
source: "horizontal-3",
type: "smoothstep",
target: "horizontal-6",
animated: true
},
{
id: "horizontal-e5-7",
source: "horizontal-5",
type: "smoothstep",
target: "horizontal-7",
animated: true
},
{
id: "horizontal-e6-8",
source: "horizontal-6",
type: "smoothstep",
target: "horizontal-8",
animated: true
}
];
export default function TrialFlowContent({ width, height }) {
// 图操作
const [nodes, setNodes, onNodesChange] = useNodesState(initialNodes);
const [edges, setEdges, onEdgesChange] = useEdgesState(initialEdges);
const onEdgeUpdate = useCallback((oldEdge, newConnection) => setEdges(els => updateEdge(oldEdge, newConnection, els)), []);
// const onConnect = useCallback(params => setEdges(els => addEdge(params, els)), []);
const nodeTypes = useMemo(() => ({ textUpdater: CuFlowNode }), []);
const onConnect = () => {
// 禁止手动连线
return;
};
return (
);
}
tyarn add dagre
// dagre 数据
const dagreGraph = new dagre.graphlib.Graph();
dagreGraph.setDefaultEdgeLabel(() => ({}));
const nodeWidth = 172;
const nodeHeight = 36;
const getLayoutedElements = (nodes, edges, direction = "TB") => {
const isHorizontal = direction === "LR";
dagreGraph.setGraph({ rankdir: direction });
nodes.forEach(node => {
dagreGraph.setNode(node.id, { width: nodeWidth, height: nodeHeight });
});
edges.forEach(edge => {
dagreGraph.setEdge(edge.source, edge.target);
});
dagre.layout(dagreGraph);
nodes.forEach(node => {
const nodeWithPosition = dagreGraph.node(node.id);
node.targetPosition = isHorizontal ? "left" : "top";
node.sourcePosition = isHorizontal ? "right" : "bottom";
// We are shifting the dagre node position (anchor=center center) to the top left
// so it matches the React Flow node anchor point (top left).
node.position = {
x: nodeWithPosition.x - nodeWidth / 2,
y: nodeWithPosition.y - nodeHeight / 2
};
return node;
});
return { nodes, edges };
};
const setTreeTopoData = (nodes, edges, direction = "TB") => {
const { nodes: layoutedNodes, edges: layoutedEdges } = getLayoutedElements(nodes, edges, direction);
setNodes([...layoutedNodes]);
setEdges([...layoutedEdges]);
};
useEffect(() => {
setTreeTopoData(nodes, edges);
}, []);
import React, { useCallback } from "react";
import ReactFlow, { useNodesState, useEdgesState, updateEdge } from "reactflow";
import { TrialCmdWrapper } from "@/pages/trial/style";
import { FlowContent } from "./style";
import "reactflow/dist/style.css";
import { useRef } from "react";
import { useEffect } from "react";
import { useMemo } from "react";
import CuFlowNode from "@/components/Home/resTopo/topo/node";
import dagre from "dagre";
const initialNodes = [
{
id: "root",
type: "input",
data: { label: "全局节点" },
position: { x: 0, y: 0 }
},
{
id: "horizontal-2",
sourcePosition: "right",
targetPosition: "left",
data: { label: "A Node" },
position: { x: 250, y: 0 }
},
{
id: "horizontal-3",
sourcePosition: "right",
targetPosition: "left",
data: { label: "Node 3" },
position: { x: 250, y: 160 }
},
{
id: "horizontal-4",
sourcePosition: "right",
targetPosition: "left",
data: { label: "Node 4" },
position: { x: 500, y: 0 }
},
{
id: "horizontal-5",
sourcePosition: "top",
targetPosition: "bottom",
data: { label: "Node 5" },
position: { x: 500, y: 100 }
},
{
id: "horizontal-6",
sourcePosition: "bottom",
targetPosition: "top",
data: { label: "Node 6" },
position: { x: 500, y: 230 }
},
{
id: "horizontal-7",
sourcePosition: "right",
targetPosition: "left",
data: { label: "Node 7" },
position: { x: 750, y: 50 }
},
{
id: "horizontal-8",
sourcePosition: "right",
targetPosition: "left",
data: { label: "Node 8" },
position: { x: 750, y: 300 }
}
];
const initialEdges = [
{
id: "horizontal-e1-2",
source: "root",
type: "smoothstep",
target: "horizontal-2",
animated: true
},
{
id: "horizontal-e1-3",
source: "root",
type: "smoothstep",
target: "horizontal-3",
animated: true
},
{
id: "horizontal-e1-4",
source: "horizontal-2",
type: "smoothstep",
target: "horizontal-4",
label: "edge label"
},
{
id: "horizontal-e3-5",
source: "horizontal-3",
type: "smoothstep",
target: "horizontal-5",
animated: true
},
{
id: "horizontal-e3-6",
source: "horizontal-3",
type: "smoothstep",
target: "horizontal-6",
animated: true
},
{
id: "horizontal-e5-7",
source: "horizontal-5",
type: "smoothstep",
target: "horizontal-7",
animated: true
},
{
id: "horizontal-e6-8",
source: "horizontal-6",
type: "smoothstep",
target: "horizontal-8",
animated: true
}
];
// dagre 数据
const dagreGraph = new dagre.graphlib.Graph();
dagreGraph.setDefaultEdgeLabel(() => ({}));
const nodeWidth = 172;
const nodeHeight = 36;
const getLayoutedElements = (nodes, edges, direction = "TB") => {
const isHorizontal = direction === "LR";
dagreGraph.setGraph({ rankdir: direction });
nodes.forEach(node => {
dagreGraph.setNode(node.id, { width: nodeWidth, height: nodeHeight });
});
edges.forEach(edge => {
dagreGraph.setEdge(edge.source, edge.target);
});
dagre.layout(dagreGraph);
nodes.forEach(node => {
const nodeWithPosition = dagreGraph.node(node.id);
node.targetPosition = isHorizontal ? "left" : "top";
node.sourcePosition = isHorizontal ? "right" : "bottom";
// We are shifting the dagre node position (anchor=center center) to the top left
// so it matches the React Flow node anchor point (top left).
node.position = {
x: nodeWithPosition.x - nodeWidth / 2,
y: nodeWithPosition.y - nodeHeight / 2
};
return node;
});
return { nodes, edges };
};
export default function TrialFlowContent({ width, height }) {
// 拖拽相关
const dropDomRef = useRef(null);
const setTreeTopoData = (nodes, edges, direction = "TB") => {
const { nodes: layoutedNodes, edges: layoutedEdges } = getLayoutedElements(nodes, edges, direction);
setNodes([...layoutedNodes]);
setEdges([...layoutedEdges]);
};
// 图操作
const [nodes, setNodes, onNodesChange] = useNodesState(initialNodes);
const [edges, setEdges, onEdgesChange] = useEdgesState(initialEdges);
const onEdgeUpdate = useCallback((oldEdge, newConnection) => setEdges(els => updateEdge(oldEdge, newConnection, els)), []);
// const onConnect = useCallback(params => setEdges(els => addEdge(params, els)), []);
const nodeTypes = useMemo(() => ({ textUpdater: CuFlowNode }), []);
const onConnect = () => {
// 禁止手动连线
return;
};
useEffect(() => {
setTreeTopoData(nodes, edges);
}, []);
return (
);
}
通过dagre,实现了自动树形布局的功能
react-flow基础使用及dagre库的使用-小何博客前言最近项目中需要用到拓扑图的展示,最开始选用的是antv的拓扑图组件。antv组件虽然此很方便,但是在布局的时候总是会有莫名其妙的bug,然后自己也想法去解决(看前辈经验、官方issue),最后还是不能解决。于…https://ligo100.cn/qianduanjishu/531.html