赞
踩
最近项目中需要用到拓扑图的展示,最开始选用的是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 (
- <TrialCmdWrapper
- height={height}
- width={width}
- style={{
- padding: 0,
- float: "left",
- marginLeft: "15px"
- }}>
- <FlowContent>
- <ReactFlow
- nodeTypes={nodeTypes}
- nodes={nodes}
- edges={edges}
- onNodesChange={onNodesChange}
- onEdgesChange={onEdgesChange}
- onConnect={onConnect}
- onEdgeUpdate={onEdgeUpdate}></ReactFlow>
- </FlowContent>
- </TrialCmdWrapper>
- );
- }

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 (
- <TrialCmdWrapper
- height={height}
- width={width}
- style={{
- padding: 0,
- float: "left",
- marginLeft: "15px"
- }}>
- <FlowContent ref={dropDomRef}>
- <ReactFlow
- nodeTypes={nodeTypes}
- nodes={nodes}
- edges={edges}
- onNodesChange={onNodesChange}
- onEdgesChange={onEdgesChange}
- onConnect={onConnect}
- onEdgeUpdate={onEdgeUpdate}></ReactFlow>
- </FlowContent>
- </TrialCmdWrapper>
- );
- }

通过dagre,实现了自动树形布局的功能
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。