react-flow基础使用及dagre库的使用

前言

最近项目中需要用到拓扑图的展示,最开始选用的是antv的拓扑图组件。antv组件虽然此很方便,但是在布局的时候总是会有莫名其妙的bug,然后自己也想法去解决(看前辈经验、官方issue),最后还是不能解决。于是更换了组件库,也就是我们今天的主角:react-flow。

react-flow简介

React Flow 是一个基于 React 的用于构建可视化流程图和图形编辑器的库。它提供了一个灵活的、可扩展的组件集合,使开发者可以轻松地创建交互式的流程图和图形编辑器应用。

react-flow特点

  1. 可视化流程图: React Flow 提供了一个易于使用的组件集合,用于创建流程图和图形编辑器。您可以使用这些组件来构建节点、边缘和连接线,以及定义它们之间的关系。
  2. 拖放支持: React Flow 支持节点和边缘的拖放操作,使用户可以方便地调整它们的位置和连接关系。您可以自定义节点和边缘的外观和行为,以适应您的应用需求。
  3. 布局算法: React Flow 内置了多种布局算法(部分收费),例如树形布局、网格布局和自由布局,可以帮助您自动排列和布局图形元素。这些算法可以根据图形的结构和属性自动调整节点的位置,使图形看起来更整齐和美观。
  4. 交互性: React Flow 提供了丰富的交互功能,包括缩放、平移、选择、多选、编辑和删除等。您可以根据需要启用或禁用这些交互功能,以创建符合用户需求的可交互图形编辑器。
  5. 事件处理: React Flow 提供了事件处理机制,允许您监听和响应图形元素的各种事件,例如节点的拖动、边缘的创建和删除等。您可以根据这些事件来实现自定义的业务逻辑和交互行为。
  6. 可定制性: React Flow 具有高度可定制性,您可以通过自定义组件、样式和配置选项来调整其外观和行为。这使得您可以根据自己的设计和需求来创建独特的图形编辑器应用。

react-flow例子

安装

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

 效果

dagre布局库使用

安装

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

展示效果

通过dagre,实现了自动树形布局的功能

原文地址

react-flow基础使用及dagre库的使用-小何博客前言最近项目中需要用到拓扑图的展示,最开始选用的是antv的拓扑图组件。antv组件虽然此很方便,但是在布局的时候总是会有莫名其妙的bug,然后自己也想法去解决(看前辈经验、官方issue),最后还是不能解决。于…https://ligo100.cn/qianduanjishu/531.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/19769.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【RuoYi-Cloud-Plus】学习笔记 09 - Sentinel(四)熔断降级知识整理

文章目录 前言参考目录版本说明学习笔记1、包结构2、DegradeSlot3、DegradeRule4、DegradeRuleManager5、CircuitBreaker5.1 CircuitBreaker.State6、AbstractCircuitBreaker6.1、AbstractCircuitBreaker#fromCloseToOpen6.2、AbstractCircuitBreaker#fromHalfOpenToOpen6.3、A…

Edge浏览器无法展示Stable Diffusion Control Net插件

Edge浏览器无法展示Stable Diffusion Control Net插件 最近在学习Stable Diffusion&#xff0c;需要使用到Control Net插件&#xff0c;结果通过各种方式安装成功插件后&#xff0c;浏览器页面没有展示ControlNet相关页面&#xff0c;最终换到Chorme浏览器后正常&#xff0c;猜…

安全测试方法介绍(上)静态源代码审查

软件开发完成之后&#xff0c;我们需要进行安全验证&#xff0c;验证我们的软件是不是符合安全要求。软件安全测试主要有以下几个方面&#xff1a;确定软件的安全特性实现是否与预期设计一致的过程&#xff1b;有关验证软件安全等级和识别潜在安全缺陷的过程&#xff1b;查找软…

云服务器下WordPress发送邮件的设置

WordPress的邮件功能很强大&#xff0c;可以实现用户密码以往后自助恢复等问题。 WordPress默认是使用php发邮件的&#xff0c;php需要配置好smtp&#xff08;端口25&#xff09;服务器及密码。这种方式不直观&#xff0c;因此一般都用smtp插件&#xff0c;常用的插件是WP Mai…

6.2.6 网络基本服务----电子邮件系统(E-mail)

6.2.6 网络基本服务----电子邮件系统&#xff08;E-mail&#xff09; 电子邮件系统是因特网上使用的最多且最受用户欢迎的一种应用 电子邮件系统包括用户代理和邮件服务器&#xff0c;提供收发邮件、邮件传送服务&#xff0c;邮件被传送到收信人邮件服务器的收信人信箱中&…

使用STM32实现 蓝牙插座

硬件介绍 蓝牙模块HC-01&#xff0c;其实之前就用过&#xff0c;使用起来非常简单 继电器模块&#xff0c; (VCC 3.3V)当左侧IN输入低电平时&#xff0c;右侧的ON 和 COM会导通&#xff0c;左上的绿灯会亮&#xff0c;此处充当插座的角色 项目需求 通过蓝牙的串口发送open打开…

8086变址寄存器和字符串拷贝程序学习

在此文用到了si和di寄存器&#xff1b; 8086汇编语言显示带颜色的字符串程序学习 - 使用emu8086_bcbobo21cn的博客-CSDN博客 下面单独来学习si和di寄存器&#xff1b; SI和DI是8086CPU中和BX功能相近的寄存器&#xff1b; 区别:SI和DI不能够分成两个8位寄存器来使用&#xff…

docker安装的mysql更改全文检索分词配置

这里使用的是mysql8.0&#xff0c;默认使用ngram分词 这里是已经将文件从容器中挂载出来了&#xff0c;没挂载出来要去容器内部更改my.cnf文件并重启mysql容器 步骤 一、查看mysql的分词大小 show variables like %token%;ngram_token_size这里默认是2我已经改为1了 这个值…

本地运行Segment Anything

按原项目GitHub - facebookresearch/segment-anything: The repository provides code for running inference with the SegmentAnything Model (SAM), links for downloading the trained model checkpoints, and example notebooks that show how to use the model.步骤 Ins…

【C++初阶】C++入门——引用

文章目录 一、引用的概念二、共用同一块空间验证三、引用的特性3.1 引用在定义时必须初始化3.2 一个变量可以有多个引用3.3 引用不能改变 四、引用的使用场景4.1 做参数4.2 做返回值 五、传值、传引用效率比较六、常引用6.1 权限放大——不被允许6.2 权限平移6.3 权限缩小6.4 赋…

springboot+mysql财务管理系统

财务管理系统的开发运用java技术、springboot框架&#xff0c;MIS的总体思想&#xff0c;以及Mysql等技术的支持下共同完成了该系统的开发&#xff0c;实现了财务管理的信息化&#xff0c;使员工体验到更优秀的财务管理&#xff0c;管理员管理操作将更加方便&#xff0c;实现目…

Mysql——》数据目录

推荐链接&#xff1a; 总结——》【Java】 总结——》【Mysql】 总结——》【Redis】 总结——》【Kafka】 总结——》【Spring】 总结——》【SpringBoot】 总结——》【MyBatis、MyBatis-Plus】 总结——》【Linux】 总结——》【MongoD…