跳到主要内容

布局库

我们经常被问到如何处理 React Flow 中的布局。虽然我们可以在 React Flow 中构建一些基本的布局,但我们相信您最了解您的应用程序的需求,并且有如此多的选项,我们认为您最好选择最适合您的工作的工具(更不用说它是一个整体)为我们做了大量的工作)。

如果您不知道这些选项是什么,这并没有多大帮助,所以本指南可以为您提供帮助!我们将把资源分成用于布局节点的资源和用于路由边缘的资源。

首先,让我们整理一个简单的示例流程,我们可以将其用作测试不同布局选项的基础。

参考预览:https://codesandbox.io/p/sandbox/jolly-kalam-tq4lc4?file=%2FApp.js&utm_medium=sandpack

下面的每个示例都将构建在这个空流程上。我们尽可能将示例限制在一个 index.js 文件中,以便您可以轻松比较它们的设置方式。

布局节点

对于布局节点,有一些我们认为值得检查的第三方库:

LibraryDynamic node sizesSub-flow layoutingEdge routingBundle size
DagreYesYes¹Nohttps://pkg-size.dev/@dagrejs/dagre
D3-HierarchyNoNoNohttps://pkg-size.dev/d3-hierarchy
D3-ForceYesNoNohttps://pkg-size.dev/d3-force
ELKYesYesYeshttps://pkg-size.dev/elkjs

Dagre 目前有一个未解决的问题,如果子流中的任何节点连接到子流外部的节点,该问题将阻止其正确布局子流。

我们对这些选项从最简单到最复杂进行了松散的排序,其中 dagre 很大程度上是一个嵌入式解决方案,而 elkjs 是一个成熟的高度可配置的布局引擎。下面,我们将看一个简短的示例,了解如何将每个库与 React Flow 一起使用。特别是对于 dagre 和 elkjs,我们有一些单独的示例,您可以参考此处和此处。

Dagre

Dagre 是一个用于布局有向图的简单库。它具有最少的配置选项,并且注重速度而不是选择最佳布局。如果您需要将流程组织成树,我们强烈推荐 dagre。

参考预览:https://codesandbox.io/p/sandbox/focused-mcclintock-8vs3kw?file=%2FApp.js&utm_medium=sandpack

不费吹灰之力,我们就得到了组织良好的树形布局!每当调用 getLayoutedElements 时,我们都会重置 dagre 图并根据 Direction 属性设置图的方向(从左到右或从上到下)。 Dagre 需要知道每个节点的尺寸才能对它们进行布局,因此我们迭代节点列表并将它们添加到 Dagre 的内部图中。

布局图形后,我们将返回一个带有布局节点和边的对象。我们通过映射原始节点列表并根据 dagre 图中存储的节点更新每个节点的位置来做到这一点。

可以在此处找到 dagre 配置选项的文档,包括用于设置间距和对齐的属性。

D3-Hierarchy

当您知道您的图形是具有单个根节点的树时,d3-hierarchy 可以提供一些有趣的布局选项。虽然该库可以很好地布局简单的树,但它还具有用于树图、分区布局和外壳图的布局算法。

参考预览:https://codesandbox.io/p/sandbox/upbeat-rumple-fnm8l4?file=%2FApp.js&utm_medium=sandpack

提示

D3-hierarchy 期望您的图表具有单个根节点,因此它并非在所有情况下都有效。同样重要的是要注意,d3-hierarchy 在计算布局时为所有节点分配相同的宽度和高度,因此如果您要显示许多不同的节点类型,它不是最佳选择。

D3-Force

强制一些比树更有趣的东西,强制导向的布局可能是正确的选择。 D3-Force 是一个基于物理的布局库,可以通过向节点施加不同的力来使用位置节点。

因此,与 dagre 和 d3-hierarchy 相比,它的配置和使用稍微复杂一些。重要的是,d3-force 的布局算法是迭代的,因此我们需要一种方法来跨多个渲染持续计算布局。

首先,让我们看看它做了什么:

参考预览:https://codesandbox.io/p/sandbox/jovial-shadow-8k4n88?file=%2FApp.js&utm_medium=sandpack

我们已将 getLayoutedElements 更改为名为 useLayoutedElements 的挂钩。此外,我们将使用 useReactFlow 挂钩中的 get getNodes 和 getEdges 函数,而不是显式传递节点和边。当与初始化的存储选择器结合使用时,这一点很重要,因为它将阻止我们在节点更新时重新配置模拟。

模拟配置了许多不同的力,以便您可以看到它们如何相互作用:在您自己的代码中尝试一下,看看您想要如何配置这些力。您可以在此处找到 d3-force 的文档和一些不同示例。

提示

矩形碰撞 D3-Force 具有内置碰撞力,但它假设节点是圆形。我们在碰撞.js 中组合了一个自定义力,它使用类似的算法,但考虑了我们的矩形节点。请随意窃取它,或者如果您有任何改进建议,请告诉我们!

tick 函数使模拟前进一步,然后使用新的节点位置更新 React Flow。我们还提供了有关如何在模拟运行时处理节点拖动的演示:如果您的流程不是交互式的,您可以忽略该部分!

提示

对于较大的图形,永远计算每次渲染的力布局将导致性能大幅下降。在此示例中,我们有一个简单的切换来打开和关闭布局,但您可能需要想出一些其他方法来仅在必要时计算布局。

Elkjs

Elkjs 无疑是最可配置的选项,但它也是最复杂的。 Elkjs 是一个已移植到 JavaScript 的 Java 库,它提供了大量用于配置图形布局的选项。

参考预览:https://codesandbox.io/p/sandbox/recursing-sound-npflwt?file=%2FApp.js&utm_medium=sandpack

最基本的是,我们可以计算类似于 dagre 的布局,但由于布局算法异步运行,我们需要创建一个 useLayoutedElements 挂钩,类似于我们为 d3-force 创建的挂钩。

提示

ELK 参考是您新的最好的朋友我们不经常推荐 elkjs,因为它的复杂性使我们很难在人们需要时提供支持。如果您决定使用它,您将需要保留原始的 Java API 参考。

我们还提供了一些其他可用布局算法的示例,包括非交互力布局。

荣誉提名

当然,我们无法遍历所有布局库:我们永远不会处理其他任何事情!以下是我们遇到的其他一些可能值得一看的库:

  • 如果您想使用 dagre 或 d3-hierarchy 但需要支持不同维度的节点,那么 d3-flextree和 entitree-flex看起来很有前途。
  • Cola.js看起来像是所谓的“基于约束”布局的一个有前途的选择。我们还没有时间对其进行适当的研究,但看起来您可以实现与 d3-force 类似的结果,但具有更多的控制能力。

Routing Edges

如果您对边缘布线没有任何要求,则可以使用上面的布局库之一来定位节点并让边缘落在任何可能的地方。否则,您将需要研究一些用于边缘路由的库和技术。

您在这里的选择比节点布局更加有限,但这里有一些我们认为看起来很有前途的资源:

  • react-flow-smart-edge
  • Routing Orthogonal Diagram Connectors in JavaScript

如果您确实探索了一些自定义边缘路由选项,请考虑通过撰写博客文章或创建库来回馈社区!