Skip to Content
教程布局概述

概述

🌐 Overview

我们经常被问到如何在 React Flow 中处理布局。我们还没有实现自己的布局解决方案,但将在此页面上介绍一些可行的外部库。我们将把内容分为用于布局节点的资源和用于路由边的资源。

🌐 We regularly get asked how to handle layouting in React Flow. We have not implemented our own layouting solution yet, but will present some viable external libraries on this page. We’ll split things up into resources for layouting nodes and resources for routing edges.

你可以在我们的在线运行 中测试一些布局选项,或者看看我们整理的示例

🌐 You can test out some of the layouting options in our playground  or have a look at the examples we’ve put together.

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

🌐 To start let’s put together a simple example flow that we can use as a base for testing out the different layouting options.

接下来的每个示例都将建立在这个空流程上。我们尽可能尝试将示例限制在一个 index.js 文件中,这样你可以轻松比较它们是如何设置的。

🌐 Each of the examples that follow will be built on this empty flow. Where possible we’ve tried to keep the examples confined to just one index.js file so it’s easy for you to compare how they’re set up.

布局节点

🌐 Layouting nodes

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

🌐 For layouting nodes, there are a few third-party libraries that we think are worth checking out:

LibraryDynamic node sizesSub-flow layoutingEdge routingBundle size
Dagre YesYes¹No
D3-Hierarchy NoNoNo
D3-Force YesNoNo
ELK YesYesYes

¹ Dagre 当前有一个未解决的问题 ,如果子流中的任何节点与子流外的节点相连,它就无法正确布局子流。

我们大致将这些选项按从最简单到最复杂的顺序排列,其中 dagre 大体上是一个即插即用的解决方案,而 elkjs 则是一个功能齐全、高度可配置的布局引擎。下面,我们将简要查看每个库如何与 React Flow 一起使用的示例。对于 dagre 和 elkjs,我们有一些单独的示例,你可以在这里 here 和这里 here 查看。

🌐 We’ve loosely ordered these options from simplest to most complex, where dagre is largely a drop-in solution and elkjs is a full-blown highly configurable layouting engine. Below, we’ll take a look at a brief example of how each of these libraries can be used with React Flow. For dagre and elkjs specifically, we have some separate examples you can refer back to here and here.

Dagre

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

🌐 Dagre is a simple library for layouting directed graphs. It has minimal configuration options and a focus on speed over choosing the most optimal layout. If you need to organize your flows into a tree, we highly recommend dagre.

我们无需任何努力就能得到一个组织良好的树布局!每当调用 getLayoutedElements 时,我们将重置 dagre 图并根据 direction 属性设置图的方向(从左到右或从上到下)。Dagre 需要知道每个节点的尺寸才能进行布局,因此我们遍历节点列表并将它们添加到 dagre 的内部图中。

🌐 With no effort at all we get a well-organized tree layout! Whenever getLayoutedElements is called, we’ll reset the dagre graph and set the graph’s direction (either left-to-right or top-to-bottom) based on the direction prop. Dagre needs to know the dimensions of each node in order to lay them out, so we iterate over our list of nodes and add them to dagre’s internal graph.

在布置好图表之后,我们将返回一个包含布局节点和边的对象。我们通过遍历原始节点列表并根据存储在 dagre 图中的节点更新每个节点的位置来实现这一点。

🌐 After laying out the graph, we’ll return an object with the layouted nodes and edges. We do this by mapping over the original list of nodes and updating each node’s position according to node stored in the dagre graph.

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

🌐 Documentation for dagre’s configuration options can be found here , including properties to set for spacing and alignment.

D3-Hierarchy

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

🌐 When you know your graph is a tree with a single root node, d3-hierarchy can provide a handful of interesting layouting options. While the library can layout a simple tree just fine, it also has layouting algorithms for tree maps, partition layouts, and enclosure diagrams.

D3-hierarchy 期望你的图有一个单一的根节点,所以它并不适用于所有情况。还需要注意的是,d3-hierarchy 在计算布局时会给_所有_节点分配相同的宽度和高度,因此如果你显示许多不同类型的节点,它可能不是最佳选择。

D3-Force

对于比树更有趣的东西,基于力的布局可能是可行的选择。D3-Force 是一个基于物理的布局库,可通过对节点施加不同的力来确定它们的位置。

🌐 For something more interesting than a tree, a force-directed layout might be the way to go. D3-Force is a physics-based layouting library that can be used to position nodes by applying different forces to them.

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

🌐 As a consequence, it’s a little more complicated to configure and use compared to dagre and d3-hierarchy. Importantly, d3-force’s layouting algorithm is iterative, so we need a way to keep computing the layout across multiple renders.

首先,让我们看看它的作用:

🌐 First, let’s see what it does:

我们已将我们的 getLayoutedElements 改为一个名为 useLayoutedElements 的 hook。此外,我们将不再显式传入节点和边,而是使用 useReactFlow hook 中的 getNodesgetEdges 函数。当与 initialized 中的 store 选择器结合使用时,这一点非常重要,因为它可以防止每次节点更新时重新配置模拟。

🌐 We’ve changed our getLayoutedElements to a hook called useLayoutedElements instead. Additionally, instead of passing in the nodes and edges explicitly, we’ll use get getNodes and getEdges functions from the useReactFlow hook. This is important when combined with the store selector in initialized because it will prevent us from reconfiguring the simulation any time the nodes update.

模拟配置了多种不同的力作用,以便你可以看到它们如何相互作用:在你自己的代码中尝试,看看你希望如何配置这些力。你可以在这里找到 d3-force 的文档和一些不同的示例 here 

🌐 The simulation is configured with a number of different forces applied so you can see how they interact: play around in your own code to see how you want to configure those forces. You can find the documentation and some different examples of d3-force here .

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

tick 函数将模拟推进一步,然后用新的节点位置更新 React Flow。我们还包括了一个演示,展示在模拟运行时如何处理节点拖动:如果你的流程不是交互式的,你可以忽略那部分内容!

🌐 The tick function progresses the simulation by one step and then updates React Flow with the new node positions. We’ve also included a demonstration on how to handle node dragging while the simulation is running: if your flow isn’t interactive you can ignore that part!

对于较大的图表,每次渲染时都计算力布局会导致很大的性能开销。在这个示例中,我们有一个简单的切换来打开和关闭布局,但你可能想要想出其他方法,仅在必要时计算布局。

Elkjs

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

🌐 Elkjs is certainly the most configurable option available, but it’s also the most complicated. Elkjs is a Java library that’s been ported to JavaScript, and it provides a huge number of options for configuring the layout of your graph.

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

🌐 At it’s most basic we can compute layouts similar to dagre, but because the layouting algorithm runs asynchronously we need to create a useLayoutedElements hook similar to the one we created for d3-force.

ELK参考是你新的最佳伙伴 我们不经常推荐elkjs,因为它的复杂性使我们在别人需要帮助时很难支持。如果你决定使用它,你会想要随身保留原始的Java API参考 

我们还包括了一些其他布局算法的示例,其中包括一个非交互式力布局。

🌐 We’ve also included a few examples of some of the other layouting algorithms available, including a non-interactive force layout.

荣誉提名

🌐 Honourable Mentions

当然,我们不可能仔细研究所有的布局库:那样我们就无暇做其他任何事情了!这里有一些我们遇到的其他库,可能值得看看:

🌐 Of course, we can’t go through every layouting library out there: we’d never work on anything else! Here are some other libraries we’ve come across that might be worth taking a look at:

  • 如果你想使用 dagre 或 d3-hierarchy,但需要支持具有不同尺寸的节点,d3-flextree entitree-flex  都看起来很有前途。

    你可以在这里找到如何在 React Flow 中使用 entitree-flex 的示例。

  • Cola.js  看起来是所谓“基于约束”的布局的一个有前途的选择。我们还没有时间去认真研究它,但看起来你可以实现类似 d3-force 的效果,同时拥有更多的控制。

路由边缘

🌐 Routing Edges

如果你对边的路径没有任何要求,你可以使用上面提到的某个布局库来定位节点,并让边随意分布。否则,你需要研究一些用于边路径的库和技术。

🌐 If you don’t have any requirements for edge routing, you can use one of the layouting libraries above to position nodes and let the edges fall wherever they may. Otherwise, you’ll want to look into some libraries and techniques for edge routing.

在这里你的选项比节点布局更有限,但这里有一些我们认为很有前途的资源:

🌐 Your options here are more limited than for node layouting, but here are some resources we thought looked promising:

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

🌐 If you do explore some custom edge routing options, consider contributing back to the community by writing a blog post or creating a library!

我们的可编辑边缘 Pro 示例也可以作为实现可以沿特定路径路由的自定义边缘的起点。

🌐 Our editable edge Pro Example could also be used as a starting point for implementing a custom edge that can be routed along a specific path.

Last updated on