react_flow自定义节点、边——使用darg布局树状结构

文章目录

    • ⭐前言
    • ⭐引入react-flow
    • ⭐自定义节点nodeType
    • ⭐自定义边edgeType
    • ⭐添加节点
    • ⭐inscode代码块
    • ⭐结束

⭐前言

大家好,我是yma16,本文分享 前端 ——react_flow自定义节点、边——使用darg布局树状结构。
自定义效果
可以自定义节点、边、线条流动
custom-flow

React Flow 简介

React Flow 是一个用于构建交互式节点和基于图的编辑器的开源 React 库。它专为可视化复杂工作流、流程图、状态机或依赖关系而设计,提供高度可定制化的节点、连线(edges)和交互功能。

node系列往期文章
node_windows环境变量配置
node_npm发布包
linux_配置node
node_nvm安装配置
node笔记_http服务搭建(渲染html、json)
node笔记_读文件
node笔记_写文件
node笔记_连接mysql实现crud
node笔记_formidable实现前后端联调的文件上传
node笔记_koa框架介绍
node_koa路由
node_生成目录
node_读写excel
node笔记_读取目录的文件
node笔记——调用免费qq的smtp发送html格式邮箱
node实战——搭建带swagger接口文档的后端koa项目(node后端就业储备知识)
node实战——后端koa结合jwt连接mysql实现权限登录(node后端就业储备知识)
node实战——koa给邮件发送验证码并缓存到redis服务(node后端储备知识)

koa系列项目文章
前端vite+vue3结合后端node+koa——实现代码模板展示平台(支持模糊搜索+分页查询)
node+vue3+mysql前后分离开发范式——实现对数据库表的增删改查
node+vue3+mysql前后分离开发范式——实现视频文件上传并渲染

koa-vue性能监控到封装sdk系列文章
性能监控系统搭建——node_koa实现性能监控数据上报(第一章)
性能监控系统搭建——vue3实现性能监控数据展示(第二章)
性能监控计算——封装带性能计算并上报的npm包(第三章)
canvas系列文章
web canvas系列——快速入门上手绘制二维空间点、线、面
webgl canvas系列——快速加背景、抠图、加水印并下载图片
webgl canvas系列——animation中基本旋转、平移、缩放(模拟冒泡排序过程)
前端vue系列文章
vue3 + fastapi 实现选择目录所有文件自定义上传到服务器
前端vue2、vue3去掉url路由“ # ”号——nginx配置
csdn新星计划vue3+ts+antd赛道——利用inscode搭建vue3(ts)+antd前端模板
认识vite_vue3 初始化项目到打包
python_selenuim获取csdn新星赛道选手所在城市用echarts地图显示
让大模型分析csdn文章质量 —— 提取csdn博客评论在文心一言分析评论区内容
前端vue3——html2canvas给网站截图生成宣传海报
前端——html拖拽原理
前端 富文本编辑器原理——从javascript、html、css开始入门
前端老古董execCommand——操作 选中文本 样式
前端如何在30秒内实现吸管拾色器?
前端——原生Selection api操作选中文本 样式、取消样式(解决标签的无限嵌套问题)
前端 ——xml转json json转xml 实现 mjml 邮件内容转json,json转mjml
前端 ——youtube、tiktok视频封面获取并使用canvas合并封面和自定义播放按钮生成图片
前端gmail邮件加载动态样式——动态评分交互邮件可提交api

⭐引入react-flow

安装@xyflow/react

pnpm add @xyflow/react

基础使用

import React from 'react';
import { ReactFlow } from '@xyflow/react';import '@xyflow/react/dist/style.css';const initialNodes = [{ id: '1', position: { x: 0, y: 0 }, data: { label: '1' } },{ id: '2', position: { x: 0, y: 100 }, data: { label: '2' } },
];
const initialEdges = [{ id: 'e1-2', source: '1', target: '2' }];export default function App() {return (<div style={{ width: '100vw', height: '100vh' }}><ReactFlow nodes={initialNodes} edges={initialEdges} /></div>);
}

⭐自定义节点nodeType

定义一个BaseNode ,可以引用flow的handle展示线条的连接点

import { Handle, Position, NodeToolbar } from '@xyflow/react';
import React, { useEffect } from 'react';
import './style.scss';const BaseNode = (props: any) => {const { data } = props;useEffect(() => {console.log('props', props);}, [props]);return (<div className="base-node"><NodeToolbar isVisible={data.toolbarVisible} position={Position.Top}><button>toolbar 按钮</button></NodeToolbar><div style={{ padding: '10px 20px' }}>{data.label}</div>{/* {data.customDataType !== 'start' ? <Handle type="source" position={Position.Top} /> : ''} */}{data.customDataType !== 'end' ? <Handle type="source" position={Position.Bottom} /> : ""}{data.customDataType !== 'start' ? <Handle type="target" position={Position.Top} /> : ""}</div >);
};export default BaseNode;

flow组件nodeType引入BaseNode

    const nodeTypes = useMemo(() => {return { textUpdater: TextUpdaterNode, AddNode: AddNode, baseNode: BaseNode };}, [TextUpdaterNode, AddNode, BaseNode]);

节点引用 type: ‘baseNode’

// 初始节点
export const initialNodes = [{id: 'start_id',position: { x: 0, y: 0 },data: { label: `开始节点_${new Date().Format('yyyy-MM-dd hh:mm:ss')}`, customDataType: 'start' },type: 'baseNode'},{id: 'test_id',position: { x: 0, y: 0 },data: { label: `测试节点_${new Date().Format('yyyy-MM-dd hh:mm:ss')}`, customDataType: 'test' },type: 'baseNode'},{id: 'end_id',position: { x: 0, y: 0 },data: { label: `结束节点_${new Date().Format('yyyy-MM-dd hh:mm:ss')}`, customDataType: 'end' },type: 'baseNode'}
];

⭐自定义边edgeType

自定义边——添加按钮

import {BaseEdge,EdgeLabelRenderer,getStraightPath,getSmoothStepPath,getSimpleBezierPath,Position,useReactFlow,
} from '@xyflow/react';import './style.scss'
import React, { useState } from 'react';export default function CustomAddEdge(props: any) {const { id, sourceX, sourceY, targetX, targetY, data } = props;console.log('CustomAddEdge props', props);console.log('CustomAddEdge props data', data);const [isShowAddPanel, setIsShowAddPanel] = useState(false);const [isSelectEdge, setIsSelectEdge] = useState(false);const [path,] = getSimpleBezierPath({sourceX: sourceX,sourceY: sourceY,// sourcePosition: Position.Top,targetX: targetX,targetY: targetY,// targetPosition: Position.Bottom,});const [edgePath, labelX, labelY] = getStraightPath({sourceX,sourceY,targetX,targetY,});return (<><BaseEdge id={id} path={path} /><circle r="10" fill="#ff0073"><animateMotion dur="2s" repeatCount="indefinite" path={edgePath} /></circle><EdgeLabelRenderer><buttonstyle={{position: 'absolute',transform: `translate(-50%, -50%) translate(${labelX}px,${labelY}px)`,pointerEvents: 'all',borderRadius: '50px',cursor: 'pointer',}}className="add-button"onClick={() => {setIsSelectEdge(true)console.log('add button clicked', props);setIsShowAddPanel(!isShowAddPanel);}}>+<div style={{display: isShowAddPanel ? 'block' : 'none',}} className='add-button-edge-panel-container'><div ><button onClick={() => {console.log('添加普通节点');data?.onAddBaseNode?.({curEdgeId: id});}} className='add-button-edge-panel'>添加普通节点</button></div><div><br></br></div><div ><button onClick={() => {console.log('添加分支节点 left');data?.onAddBranchNode?.({curEdgeId: id,direction: 'left'});}} className='add-button-edge-panel'>添加分支 保留左边</button></div><div><br></br></div><div ><button onClick={() => {console.log('添加分支节点 right');data?.onAddBranchNode?.({curEdgeId: id,direction: 'right'});}} className='add-button-edge-panel'>添加分支 保留右边</button></div></div></button></EdgeLabelRenderer></>);
}

⭐添加节点

基础节点

// 添加基础节点
export const addBaseNode = (config: { curEdgeId: string; nodes: any[]; edges: any[] }) => {const { curEdgeId, nodes, edges } = config;console.log('addBaseNode curEdgeId', curEdgeId);// 当前边的节点const curEdge = edges.find(edge => edge.id === curEdgeId);if (!curEdge) {console.error('Edge not found for id:', curEdgeId);return { nodes, edges };}// 创建新的节点 基础节点const virtualNode = {id: customGenUuid(),position: { x: 0, y: 0 },data: { label: `普通节点_${new Date().Format('yyyy-MM-dd hh:mm:ss')}`, customDataType: 'test' },type: 'baseNode'};// 新节点const newNodes: any[] = [];// 遍历节点添加  按顺序加入nodes.forEach(node => {if (node.id === curEdge.source) {// 在当前边的源节点后面添加新节点newNodes.push(virtualNode);}newNodes.push(node);});// 添加新边const newEdges: any[] = [];edges.forEach(edge => {// 如果是当前边,则添加新边  source和target 中间添加一个节点 补充一条边if (edge.id === curEdgeId) {// 在当前边后面添加新边newEdges.push({id: customGenUuid(),source: curEdge.source,// 链接当前节点target: virtualNode.id,type: 'customAddEdge',markerEnd: {type: MarkerType.Arrow}});// 在当前边后面添加新边newEdges.push({id: customGenUuid(),source: virtualNode.id,// 链接当前节点target: curEdge.target,type: 'customAddEdge',markerEnd: {type: MarkerType.Arrow}});} else {// 其他边不变newEdges.push(edge);}});return {newNodes: newNodes,newEdges: newEdges};
};

添加分支节点

// 添加分支节点  默认添加左分支
export const addBranchNode = (config: { curEdgeId: string; nodes: any[]; edges: any[]; direction: string }) => {const { curEdgeId, nodes, edges, direction } = config;console.log('addBaseNode curEdgeId', curEdgeId);// 当前边的节点const curEdge = edges.find(edge => edge.id === curEdgeId);if (!curEdge) {console.error('Edge not found for id:', curEdgeId);return { nodes, edges };}// 创建新的节点 基础节点const virtualLeftNode = {id: customGenUuid(),position: { x: 0, y: 0 },// 左边分支 节点data: { label: `分支节点_${new Date().Format('yyyy-MM-dd hh:mm:ss')}`, customDataType: 'test', branchDirection: 'left' },type: 'baseNode'};// 右边分支节点const virtualRightNode = {id: customGenUuid(),position: { x: 0, y: 0 },// 左边分支 节点data: { label: `分支节点_${new Date().Format('yyyy-MM-dd hh:mm:ss')}`, customDataType: 'test', branchDirection: 'right' },type: 'baseNode'};// 新节点const newNodes: any[] = [];// 遍历节点添加  按顺序加入nodes.forEach(node => {if (node.id === curEdge.source) {// 在当前边的源节点后面添加新节点  先添加左边在添加右边的节点if (direction === 'left') {newNodes.push(virtualLeftNode);newNodes.push(virtualRightNode);} else {// 右边分支newNodes.push(virtualRightNode);newNodes.push(virtualLeftNode);}}newNodes.push(node);});// 添加新边const newEdges: any[] = [];edges.forEach(edge => {// 如果是当前边,则添加新边  source和target 中间添加一个节点 补充一条边if (edge.id === curEdgeId) {// 在当前边后面添加新边newEdges.push({id: customGenUuid(),source: curEdge.source,// 链接当前节点target: direction === 'left' ? virtualLeftNode.id : virtualRightNode.id,data: {branchDirection: 'right'},type: 'customAddEdge',markerEnd: {type: MarkerType.Arrow}});// 在当前边后面添加新边newEdges.push({id: customGenUuid(),source: direction === 'left' ? virtualLeftNode.id : virtualRightNode.id,// 链接当前节点target: curEdge.target,data: {branchDirection: 'right'},type: 'customAddEdge',markerEnd: {type: MarkerType.Arrow}});// 添加右侧分支边newEdges.push({id: customGenUuid(),source: curEdge.source,// 链接当前节点target: direction === 'left' ? virtualRightNode.id : virtualLeftNode.id,type: 'customAddEdge',data: {branchDirection: 'right'},markerEnd: {type: MarkerType.Arrow}});} else {// 其他边不变newEdges.push(edge);}});console.log('addBranchNode newNodes', {newNodes: newNodes,newEdges: newEdges});return {newNodes: newNodes,newEdges: newEdges};
};

⭐inscode代码块

效果演示

react_flow特点

节点与连线:支持自定义节点(矩形、圆形等)和动态连线(贝塞尔曲线、直线等)。
交互功能:拖拽节点、缩放画布、选择多个元素、键盘快捷键支持。
状态管理:与外部状态库(如 Redux、Zustand)无缝集成。
插件系统:内置背景网格、迷你地图、节点工具栏等插件。
性能优化:仅渲染可见区域的节点,适合大规模数据场景。
react-flow存在的问题
React Flow在处理大规模节点和连线时可能出现性能瓶颈,尤其是当画布包含数百个节点时,渲染和交互可能变得迟缓。动态添加或删除节点时的重绘操作可能消耗较多资源。

⭐结束

本文分享到这结束,如有错误或者不足之处欢迎指出!

scene

👍 点赞,是我创作的动力!

⭐️ 收藏,是我努力的方向!

✏️ 评论,是我进步的财富!

💖 最后,感谢你的阅读!

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

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

相关文章

word表格批量转excel,提取表格数据到excel

本文将带你一步步实现一个将 Word 中的表格内容批量提取并转换为 Excel 文件的自动化工具&#xff0c;适用于需要手动复制粘贴数据到excel的场景 假设我们有这样的表格在word中&#xff0c;图片世放在excel中便于截图&#xff0c;现在需要将表格中有颜色的数据提取到对应的exce…

day2课程

1.添加pinia到Vue项目 2.counter基础使用 3.getters和异步action 4.storeToRefs和调试 5.项目初始化和git管理 6.别名路径联想设置 7.elementsPlus自动按需导入配置 这个项目使用的是按需引入 1.安装包管理器 npm install element-plus --save 2.按需引入 npm install -D unp…

Vue3 + TypeScript + Element Plus 设置表格行背景颜色

技术要点&#xff1a; 1、使用 :row-class-name"setRowClassName" 设置表格行类名 2、不能同时使用 stripe 3、设置行类名的样式 应用效果&#xff1a; 同时使用 stripe 出来的效果&#xff1a; 参考代码&#xff1a; ReagentTable.vue <script setup lang&…

山东大学 软件项目管理知识点总结

软件项目管理背诵总结 将老师所发ppt的知识点整理&#xff0c;方便查阅与背诵。 文章目录 软件项目管理背诵总结1. 概述1.1 什么是项目&#xff1f;1.2 项目有那些特征&#xff1f;1.3 项目于日常工作有什么区别&#xff1f;1.4 如何衡量一个项目是否成功&#xff1f;1.5 软件项…

css基础笔记简洁版1

&#x1f4d8; CSS 基础笔记 1 一、CSS 简介 CSS&#xff08;层叠样式表&#xff09;用于为网页添加样式&#xff0c;实现结构与样式分离&#xff0c;能够控制颜色、字体、布局、位置、动画等视觉效果。 二、基本语法 选择器 {属性1: 值1;属性2: 值2; }说明&#xff1a; 选…

reactor模型学习

学习链接 狂野架构师第四期netty视频 - B站视频 狂野架构师训练营6期 - B站视频 Netty学习example示例&#xff08;含官方示例代码&#xff09; LG-Netty学习 【硬核】肝了一月的Netty知识点 - 启动过程写的很详细 Reactor模型讲解 一文搞懂Reactor模型与实现 高性能网络编…

应用探析|千眼狼高速摄像机、sCMOS相机、DIC测量、PIV测量在光学领域的应用

2025&#xff0c;长春&#xff0c;中国光学学会学术大会。中科视界携千眼狼品牌四大科学仪器高速摄像机、sCMOS科学相机、DIC应变测量系统、PIV流场测量系统亮相&#xff0c;在光学领域多个细分研究方向承载科学实验的感知与测量任务。 1先进制造技术及其应用 激光切割、激光焊…

Kafka 4.0.0集群部署

Kafka 4.0.0集群部署 1.1 关闭防火墙和 selinux 关闭防火墙 systemctl stop firewalld.service systemctl disable firewalld.service关闭selinux setenforce 0 #&#xff08;临时生效&#xff09; sed -i s/^SELINUXenforcing/SELINUXdisabled/ /etc/selinux/config #&…

探秘卷积神经网络(CNN):从原理到实战的深度解析

在图像识别、视频处理等领域&#xff0c;卷积神经网络&#xff08;Convolutional Neural Network&#xff0c;简称 CNN&#xff09;如同一位 “超级侦探”&#xff0c;能够精准捕捉图像中的关键信息&#xff0c;实现对目标的快速识别与分析。从医疗影像诊断到自动驾驶中的路况感…

vue3导入xlsx表格处理数据进行渲染

下载插件 npm install -S xlsx import * as XLSX from "xlsx"; // Vue3 版本 <el-upload class"upload-demo"accept".xlsx":http-request"channel":show-file-list"false":limit"1"><el-button type&qu…

生成模型_条件编码器

条件编码器可以采用不同的网络结构&#xff0c;UNet 是其中非常常见的一种&#xff0c;尤其在 Diffusion 和图像生成任务中用得最多。 &#x1f9e0; 什么是“条件编码器”&#xff1f; 在 **条件生成模型&#xff08;Conditional GAN / Diffusion&#xff09;**中&#xff0c…

@Scheduled, @PostConstruct, @PreDestroy, @Async, @OnApplicationEvent

注解名称模块功能引入年份版本是否推荐使用PostConstructjavax.annotation (Java EE) / spring-beansBean 初始化完成后执行的方法2006Java EE 5 / Spring 2.0✔️ 推荐PreDestroyjavax.annotation (Java EE) / spring-beansBean 销毁前执行的方法2006Java EE 5 / Spring 2.0✔…

小程序请求加载提示防闪烁机制详解

目录 一、问题背景&#xff1a;闪烁现象的产生 二、完整解决方案代码 三、核心防闪烁机制解析 1. 请求计数器&#xff08;requestCount&#xff09; 2. 延迟隐藏定时器&#xff08;关键创新&#xff09; 3. 100ms缓冲期的重要意义 四、关键场景对比分析 场景1&#xff…

linux防火墙讲解

目录 安全管理 一、SELinux安全上下文 1、SELinux 简介 2、基础操作命令 1. 查看SELinux状态 2. 切换工作模式* 3、安全上下文&#xff08;Security Context&#xff09; 1. 查看上下文* 2. 修改上下文 chcon命令 semanage 命令 4、SELinux布尔值&#xff08;Boole…

巧用 Python:将 A3 作业 PDF 轻松转为 A4 可打印格式

在孩子的学习过程中&#xff0c;我们常常会遇到这样的困扰&#xff1a;学校老师发的作业是以 A3 格式的 PDF 文件呈现的&#xff0c;然而家里的打印机却只支持 A4 打印。这时候&#xff0c;要是能有一个简单的方法把 A3 的 PDF 转换为 A4 可打印的格式就好了。别担心&#xff0…

Transformer 核心概念转化为夏日生活类比

以下是把 Transformer 核心概念转化为「夏日生活类比」&#xff0c;不用看代码也能秒懂&#xff0c;搭配冰镇西瓜式记忆法&#xff1a; 一、Transformer 夏日冷饮制作流水线 编码器&#xff08;Encoder&#xff09;&#xff1a;相当于「食材处理间」 把输入&#xff08;比如…

【Linux基础知识系列】第二十九篇-基本的网络命令(ping, traceroute, netstat)

在Linux系统中&#xff0c;网络诊断是系统管理员和用户日常工作中不可或缺的一部分。无论是排查网络连接问题、检查网络延迟&#xff0c;还是监控网络状态&#xff0c;掌握一些基本的网络命令至关重要。本文将详细介绍ping、traceroute和netstat这三种常用的网络命令&#xff0…

javaee初阶-多线程

1.什么是线程 1.1 进程 要了解线程我们首先需要了解什么是进程&#xff1f; 运行的程序在操作系统中以进程的方式运行&#xff0c;比如说电脑打开不同的软件&#xff0c;软件就是不同的进程 1.1.1进程的组织方式 通过双向链表 创建进程就是在双向链表上添加PCB 销毁一个进…

N数据分析pandas基础.py

前言&#xff1a;在数据分析领域&#xff0c;Python 的 Pandas 库堪称得力助手。它不仅拥有高效的数据处理能力&#xff0c;还能与 NumPy 完美配合——后者强大的数值计算功能为 Pandas 提供了坚实的技术基础。 目录 Pandas数据分析实战&#xff1a;解锁数据处理的高效之道 数…

卫星通信链路预算之二:带宽和功带平衡

在上一个章节卫星通信链路预算之一&#xff1a;信噪比分配 中&#xff0c;我们介绍了卫星通信链路中最核心的概念&#xff1a;信噪比分配&#xff0c;并给出了卫星通信链路总信噪比的计算公式。 本篇文章&#xff0c;我们将介绍卫星通信链路中的另外一个基本概念&#xff1a;带…