Reactflow图形库结合Dagre算法实现函数资源关系图

1

概述

在进行函数计算开发时,函数资源关联关系错综复杂,为了方便用户对资源信息的全量感知,决定把函数所有资源信息用关系图的方式进行呈现。同时可灵活操作实现对资源的管控,从而提升用户体验,下面为大家介绍如何实现。

主要技术使用了Reactflow 图形库进行节点与边的绘制渲染,并结合Dagre 层次布局计算各节点的位置,从而实现最优布局。

本文将依据下图,从数据处理、布局算法、图形绘制三个方面分别阐述,并将重点介绍Dagre布局算法与Reactflow的画布绘制。

2

数据处理

当数据有一定层级结构或先后顺序时,经常会用到层次(Hierarchy)布局。函数下的资源则是以版本为维度,进行资源的关联。

  1. 获取数据:考虑到REST API接口的数据特征,使用Promise.all() 来批量处理从 REST API 获取的数据。

  2. 处理数据:根据实际需求对数据重新整合。如增加数据条目、合并资源信息等操作。

    数据核心点:增加虚拟节点

    【虚拟节点优点】:合理地增加虚拟节点,可以优化 dagre 布局的效果,使图更加美观和易于理解。

    【虚拟节点适用情况】:

    1. 长边分割:当一条边跨越多个层次时,可以通过增加虚拟节点将其分割成多条短边,保证每条边只连接相邻的两层,从而优化布局。

    2. 对齐节点:通过增加虚拟节点,可以确保某些节点在同一层次上对齐。

    3. 控制边的弯曲:虚拟节点可以帮助控制边的弯曲程度,使图的布局更加美观。

3

Reactflow图形库

Reactflow 是一个用于构建交互式图表和流程图的 React 库。它提供了一个强大的 API 和丰富的功能,使开发者能够轻松地创建、编辑和管理复杂的图表和流程图。它支持拖放、缩放、节点和边的自定义等功能,非常适合用于构建工作流编辑器、数据流图、流程图等应用。

官网基础节点/边样式:

自定义节点/边样式:

实现功能:

(1)自定义节点

Reactflow提供了基础节点,如上图的Node A 与 Node B,同时提供了nodeType属性用于自定义节点。

对于函数资源,需要考虑版本、别名、触发器、域名四种节点展开与收起状态的绘制。

(2)自定义边

Reactflow提供了多种边的绘制,如贝塞尔曲线,SmoothStep平滑曲线、曲线是否动态等功能。

对于函数资源关系图的绘制,也需要自定义边的样式。

  • 对于别名与版本的关联涉及到了别名所关联版本(即主版本和灰度版本)的关系比例,需在别名被选中时,显示与之对应版本的占比数据。

  • 边(正向和反向)上的文案位置及方向。

  • 边是否选中时的明暗渲染。

(3)句柄

所谓句柄,即上图中的实心黑点,用于连接节点和边。句柄方向需要根据关系图的延展方向(如上下或左右)确定。

typescript 复制代码
<Handle type="source" position="bottom"  /> // 定义下方句柄,如触发器/域名节点可使用,用于下一层数据的连接。
<Handle type="target" position="top"  /> // 定义上方句柄,如版本可使用,用于上一层数据的连接。

(4)节点的自定义悬浮文案:

自定义悬浮文案(Tooltip)可以通过使用 Reactflow 提供的 useReactFlow 钩子和自定义组件来实现。需保证悬浮文本框需跟随文案滚动,且不被临近节点遮挡。

问题:reactflow库限制了节点在画布中层级最高,若将自定义悬浮文案放于画布中,则会存在悬浮文案始终会被其他邻近节点遮挡的情况。

解决:考虑使用ReactDom.CreactPortal(children, document.body),将其放在与 body 同级的 Dom 中,来保证层级最高。

问题:继上一步解决后,随之而带来的问题是对于可移动的画布,需要保证无论画布如何移动,其悬浮位置总是能够在节点的下方显示,即跟随滚动。

解决:实际位置 = 画布距离视口左侧的距离+节点的初始位置+节点拖动的距离。其中节点拖动的距离可使用 useReactflow() 钩子提供的 getViewport()方法来获取。

复制代码
javascript 复制代码
const { getViewport } = useReactFlow();
 const { x: viewX, y: viewY} = getViewport(); //  x, y 即为画布拖动时节点在x,y 方向上的移动距离

文案跟随滚动效果如下:

4

Dagre的层次布局

在数据有一定层级结构或先后顺序时,经常会用到层次布局来展现,一般对应的数据结构是DAG(Directed Acyclic Graph,即有向无环图),使用dagre布局更贴合业务场景。

Dagre常用的场景包括:流程图、组织架构图、状态转移图等。

综合考虑,选择使用antv的布局算法所继承的DagreLayout布局算法。该算法是对有向无环图进行层次布局的实现。

基本概念:

(1)rankDir:图的延展方向。包括由上到下(TB)、由下到上(BT)、由左到右(LR)、由右到左(RL)四种延展方向。

(2)rank:图的层次划分。即沿着图的延展方向划分的层级。每个顶点都存在于某个层级上,一个层级可能有多个顶点。

(3)level:每一层节点的级别顺序。

实现流程:

整个算法实现布局的计算流程分为去环、节点分层、同层节点排序、绘制。

  1. 去环:Dagre使用了DFS和贪心算法确保得到完整的有向无环图;

    DFS 算法:使用 DFS 进行遍历,若在一条通路中,同一个节点被访问了多次,则该通路上存在环路,去掉成环的通路上最后一条边即可。

    贪心算法:优先反转同时存在于多个环的边,尽量减少需要反转的边。

  2. 节点分层:进行图的层次划分。选择采用紧凑树(tight-tree)的布局方式,即在最长路径树的基础上,遍历并移动各个节点,使得每个边都达到它需要的最短长度,即目的是减少长边的数量,其特点是排列紧凑,复杂度中等;

    **紧凑树算法:**首先每条边需要给定一个最小长度,比如1。然后计算松弛度delta(即边的长度 - 最小长度)。如果松弛度为0,则为紧致边。把所有紧致边和节点加入紧致生成树,每次取松弛度最小的边;如果source节点已在紧致树中,把target节点向上移delta层级;如果source节点不在紧致树中,把source节点向下移delta层级,循环直到紧致树中已经包含了所有的节点。

    **其它算法:**Dagre还提供了最长路径(long-path)和网络网格单纯形(network-simplex)两种算法,但考虑到算法复杂度和实现效果,紧凑树为最优。

  3. 同层节点排序:图中的节点已经按照 rank 分好层级,但Dagre布局比较灵活,为了计算布局最优,会出现节点顺序改变、边交叉、节点重叠的情况。所以每一个层级中的节点先后顺序仍然需要调整,以达到减少边的交叉的目的。算法会自动帮忙进行排序,若仍然出现,可自行进行节点排序,如函数下所有资源都是根据版本为维度进行数据扩展的,所以可通过"版本"层的资源节点排序,来避免这些问题。

  4. 绘制:计算出最终的节点坐标,将其输入到reactflow中进行画布节点绘制。

基于以上特征,使用@antv/layout包中的Dagre布局实现。该算法的包相对较小,直接提供了自定义节点的指定层次,初始节点的位置,且其提供了updateNodeCfg()来实现节点的预制信息,如上一次布局的位置。综合所有的布局算法,这个布局明显更符合业务需求。

如内存太大,可设置 NODE_OPTIONS 环境变量,将 Node.js 的最大旧生代内存设置为 2048 MB,以防止内存不足的问题。(export NODE_OPTIONS=--max_old_space_size=2048)

5

实践

可参考官网dagre布局的简单实现:https://v10.reactflow.dev/docs/examples/layout/dagre/

1、数据处理:增加虚拟节点类型,例如对于版本而言,版本上没有别名也没有触发器和域名,需增加一个虚拟节点。同理,对于别名列表而言,别名上没有触发器和域名,也需要增加虚拟节点来优化布局。

2、自定义节点,将其处理为dagre布局所能识别的参数

重要参数说明:layer:定义该节点的层次;layout:节点是否参与布局;size:节点的大小;type:自定义节点的类型。

节点的sourcePosition和targetPosition需要根据图的延展方向进行选择。dagre布局会根据这些数据的设置进行最优的位置计算。

3、dagre布局

配置Dagre相关参数。

重点是可通过updateCfg()进行一些信息预置,如初始位置,节点顺序。尤其是增量布局,在图新增节点和边时,尽量保持原有的布局不变,在@antv/layout中的Dagre算法实现中,有nodeOrder、preset等相关参数,可使用上一次布局的结果数据作为输入,可以进行增量布局、保证重新布局的连续性。

布局结束后,通过reactflow提供的setNode()方法,将节点位置更新。

4、绘制渲染

Reactflow提供了nodeTypes和edgeTypes进行自定义节点和自定义边的数据接口。当节点选中时,方法onNodesChange和onEdgesChange可处理节点和边的一些业务逻辑和UI更新及其它功能(如自定义画布的是否拖动、是否允许滚轮缩放等等)

6

效果

如gif图所示:

7

总结

本文对Dagre布局算法进行了有业务场景的分享,其中涉及到一些遇到的困难及解决方法,如使用增量布局来保持原布局、节点分层、减少边交叉的方法、悬浮提示框始终跟随画布的位置等等,旨在这里分享给有同样场景或感兴趣的各位同学。


推荐阅读

函数计算------文档与网页数据提取工具(MinerU)应用实践

函数计算的版本管理与灰度发布

更多技术和产品文章,请关注👆

如果您对哪个产品感兴趣,欢迎留言给我们,我们会定向邀文~

go 复制代码
360智汇云是以"汇聚数据价值,助力智能未来"为目标的企业应用开放服务平台,融合360丰富的产品、技术力量,为客户提供平台服务。
目前,智汇云提供数据库、中间件、存储、大数据、人工智能、计算、网络、视联物联与通信等多种产品服务以及一站式解决方案,助力客户降本增效,累计服务业务1000+。
智汇云致力于为各行各业的业务及应用提供强有力的产品、技术服务,帮助企业和业务实现更大的商业价值。
官网:https://zyun.360.cn 或搜索"360智汇云"
客服电话:4000052360
相关推荐
ZhaiMou17 分钟前
HTML5拖拽API学习 托拽排序和可托拽课程表
前端·javascript·学习·html5
·云扬·1 小时前
Java IO 与 BIO、NIO、AIO 详解
java·开发语言·笔记·学习·nio·1024程序员节
东方巴黎~Sunsiny2 小时前
java-图算法
java·开发语言·算法
code_shenbing3 小时前
跨平台WPF框架Avalonia教程 三
前端·microsoft·ui·c#·wpf·跨平台·界面设计
ad禥思妙想4 小时前
如何运行python脚本
开发语言·python
Matlab程序猿小助手4 小时前
【MATLAB源码-第218期】基于matlab的北方苍鹰优化算法(NGO)无人机三维路径规划,输出做短路径图和适应度曲线.
开发语言·嵌入式硬件·算法·matlab·机器人·无人机
威威猫的栗子4 小时前
用 Python 与 Turtle 创作属于你的“冰墩墩”!
开发语言·python·turtle
IT古董4 小时前
【机器学习】超简明Python基础教程
开发语言·人工智能·python·机器学习
黑客Ash4 小时前
网络安全知识点
开发语言·php
NightCyberpunk4 小时前
JavaScript学习笔记
javascript·笔记·学习