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
相关推荐
天天扭码2 分钟前
一分钟解决 | 高频面试算法题——滑动窗口最大值(单调队列)
前端·算法·面试
星释5 分钟前
ASP.NET常见安全漏洞及修复方式
前端·ui·asp.net
Bunury23 分钟前
element-plus添加暗黑模式
开发语言·前端·javascript
心走27 分钟前
八股文中TCP三次握手怎么具象理解?
前端·面试
Aiolimp36 分钟前
React常见Hooks使用(二)
前端·react.js
By北阳36 分钟前
CSS 中实现 div 居中有以下几种常用方法
前端·css
在广东捡破烂的吴彦祖39 分钟前
window配置Flutter开发环境
前端·flutter
辣椒粉丝41 分钟前
记rspack想提issuse,提太慢白嫖不上了
前端·javascript
腰间盘突出的红利43 分钟前
npm组件库搭建
前端
火星思想44 分钟前
前端基础布局写法详解:左右、左中右及弹性布局实践
前端·css