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

news2024/11/26 10:45:31

前言

最近项目中需要用到拓扑图的展示,最开始选用的是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 (
		<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>
	);
}

 效果

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 (
		<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,实现了自动树形布局的功能

原文地址

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

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【安卓12源码】WMS的作用及其启动流程

一、WMS 的作用 WMS 在 Android 系统的地位&#xff0c;它作为中间层&#xff0c;连接了上层的 View 框架和下层的 SurfaceFingler。 WMS 主要职责 窗口管理&#xff1a;负责启动、添加、删除窗口&#xff0c;管理窗口大小、层级&#xff0c;核心成员有&#xff1a;WindowCont…

短视频矩阵系统源码开发规则

抖音seo源码部署&#xff08;前端vue jquery layui 后端php&#xff09;组成 抖音SEO源码开发规则是为了提高抖音内容在搜索引擎的排名&#xff0c;增加曝光和流量而制定的一系列准则。 这些开发规则包括但不限于以下几点&#xff1a; 首先&#xff0c;优化关键词&#xff…

Mac OS 源码编译安装Nginx

下载软件 访问地址&#xff1a; https://nginx.org 根据自己的需求下载合适的安装包 首先建个临时目录 nginx-src 并下载所需软件的源码进行配置 mkdir nginx-src cd nginx-src wget https://nginx.org/download/nginx-1.18.0.tar.gz wget https://ftp.pcre.org/pub/p…

前端框架Layui的使用讲解(Layui搭建登录注册页面)

目录 一、前言 1.什么是Layui 2.Layui的背景 3.为什么要使用Layui 4.Layui的模块化 二、Layui使用讲解 1.初识Layui 2.搭建登录页面 静态效果图​ 封装引入文件页面&#xff08;公用页面&#xff09; jsp页面搭建 userDao编写 Servlet页面编写 xml文件配置 3.搭…

DevOps(一)

DevOps 1. DevOps起源1.1 瀑布开发模型1.2 敏捷开发模型 2. DevOps到底是什么&#xff1f;3. DevOps与虚拟化、容器、微服务4. CI/CD是什么 &#xff1f;4.1 CI 持续集成&#xff08;Continuous Integration&#xff09;4.2 CD 持续交付&#xff08;Continuous Delivery&#x…

JAVA基础方法-substring+indexof

substring用法&#xff1a; 1.public String substring(int beginIndex, int endIndex) 第一个参数int为开始的索引&#xff0c;对应String数字中的开始位置&#xff0c; 第二个参数是截止的索引位置&#xff0c;对应String中的结束位置。 public static void main(String[]…

react中styled-components 全局样式设置

前言 使用 styled-components 库时&#xff0c;你可以使用它的 createGlobalStyle 函数来设置全局样式。下面是一个示例&#xff1a; 安装 styled-components npm install styled-components导入 createGlobalStyle 在你的代码文件中导入 createGlobalStyle&#xff1a; i…

信息安全-应用安全-软件成分安全分析(SCA)能力的建设与演进

1. 前言 SCA 概念出现其实很久了。简单来说&#xff0c;就是针对现有的软件系统生成粒度非常细的 SBOM&#xff08;Software Bill of Materials 软件物料单&#xff09;清单&#xff0c;然后通过⻛险数据去匹配有没有存在⻛险组件被引用。目前&#xff0c;市面上比较出色的商业…

laravel6.x文档阅读手册

laravel中文文档6.x 目录 一、入门指南 安装 服务器要求 安装 Laravel Laravel 使用 Composer 来管理项目依赖。因此&#xff0c;在使用 Laravel 之前&#xff0c;请确保你的机器已经安装了 Composer。 通过 Laravel 安装器 首先&#xff0c;通过使用 Composer 安装 Lara…

Django_加载settings配置

当使用下面命令启动django服务时&#xff0c;setting会自动加载 python manage.py runserver 通过查看manage.py可以找到加载代码为 os.environ.setdefault(DJANGO_SETTINGS_MODULE, settings的路径id) 知道settings加载原理后&#xff0c;在调试时可以不用启动http服务&…

如何用 Jenkins+Docker 实现一键自动化部署

本文章实现最简单全面的Jenkinsdockerspringboot 一键自动部署项目&#xff0c;步骤齐全&#xff0c;少走坑路。 环境&#xff1a;centos7git(gitee) 简述实现步骤&#xff1a;在docker安装jenkins&#xff0c;配置jenkins基本信息&#xff0c;利用Dockerfile和shell脚本实现…

滴...这里有一道数据库操作型面试题,已到达,请查收~

系列文章传送门&#xff1a; 【七天入门数据库】第一天 MySQL的安装部署 【七天入门数据库】第二天 数据库理论基础 【七天入门数据库】第三天 MySQL的库表操作 题目&#xff1a;单表查询&#xff0c;根据提供的素材&#xff0c;按下列要求查询相关数据。 题目素材&#x…

前端vue入门(纯代码)27_路由的query参数

安静地努力&#xff01;&#xff01;&#xff01; 【25.Vue Router--路由的query参数】 多级路由在src/router/index.js中【三级路由】的配置如下&#xff1a; // 该文件专门用于创建整个应用的路由器 import VueRouter from "vue-router"; //引入组件 import Abo…

接口自动化测试要做什么?

先了解下接口测试流程&#xff1a; 1、需求分析2、Api文档分析与评审 3、测试计划编写 4、用例设计与评审5、环境搭建&#xff08;工具&#xff09; 6、执行用例 7、缺陷管理 8、测试报告 接口流程详细内容&#xff0c;请狠狠点击下面这篇文章&#xff0c; 《做接口测试的流程…

关于Vue 、vue2、vue3

vue优点&#xff1f;vue2、vue3响应式比较&#xff1f; &#xff08;1&#xff09; 响应式编程 Vue 会自动对页面中某些数据的变化做出响应。通过 MVVM 思想实现数据的双向绑定&#xff0c;让开发者不用再操作 DOM 对象&#xff0c;有更多的时间去思考业务逻辑。 组件化开发…

程序员,你喜欢写文档吗?

博主&#xff1a;爱码叔 个人博客站点&#xff1a; icodebook 公众号&#xff1a;漫话软件设计 微博&#xff1a;程序员涛哥 专注于软件设计与架构、技术管理。擅长用通俗易懂的语言讲解技术。对技术管理工作有自己的一定见解。文章会第一时间首发在个站上&#xff0c;欢迎大家…

【论文解读】A Fast Sub-pixel Motion Estimation Algorithm for H.264/AVC Video Coding

简介 题目&#xff1a;A Fast Sub-pixel Motion Estimation Algorithm for H.264/AVC Video Coding 原文&#xff1a;https://ieeexplore.ieee.org/document/5688303 级别&#xff1a;SCI 年份&#xff1a;2011 年 机构&#xff1a;上海交通大学 结论&#xff1a;亚像素搜索计…

【C++】unordered_map、unordered_set 模拟实现

文章目录 概念框架实现正反迭代器Find()、Insert() 、Erase()unordered_map 的 operator[ ] 源代码HashTable.hunordered_map.hunordered_set.h 概念 unordered_set 是含有 Key 类型唯一对象集合的关联容器。搜索、插入和移除拥有平均常数时间复杂度。在内部&#xff0c;元素并…

HTML页面通过Web3JS连接智能合约并调用其中接口

之前我们学习solidity 并用它编写了智能合约 登上区块链 然后也做了基本的测试 但是 其实在web3时代 前端依旧扮演者非常重要的角色 我们现在就来打通web3 从合约到页面的一个管理 首先 我们还是将自己的ganache环境起起来 然后 在我们之前智能合约的项目终端执行 truffle m…

Python-Go 文件操作和异常操作

目录 python的异常操作 异常是什么&#xff1f; 错误与异常的区别&#xff1a; 常见异常 Traceback 错误回溯 异常处理 异常处理的定义&#xff08;try - except&#xff09; 扩展&#xff08;可以捕获不同的错误、多except语句&#xff09;&#xff08;else - finall…