G6 Mindmap 布局(思维导图树布局)完全指南:方向、间距与左右分侧配置详解

原创2026-09-23 22:23:541,374 阅读
文章标签:数据可视化前端图表库

G6 Mindmap 布局(思维导图树布局)完全指南:方向、间距与左右分侧配置详解

导读

Mindmap(思维导图树)布局是 G6 中专门用于树形结构层级排布的布局方案,支持根节点左右双侧展开,同一深度的节点会被放置在同一个层级上。本文基于 MindmapLayout.en.md 官方文档,结合 G6 仓库内的源码与测试用例,系统讲解 mindmap 布局的全部配置项、五种方向模式的行为差异、节点尺寸/间距的计算方式,以及基于 getSide 实现上游/下游血缘图、自定义思维导图等实战方案,读完即可在项目中落地使用。

Overview:Mindmap 布局的定位与特性

Mindmap 树布局适用于树形结构(Tree)的层级排布,其核心特点有两个:

  • 支持左右双侧展开:根节点的子节点可以被分到根节点的左右两侧,形成典型的思维导图形态;
  • 同深度节点对齐:处于同一深度的节点会被放置在同一个层级(layer)上,保证层级清晰;
  • 会考虑节点尺寸:与某些忽略节点大小的树布局不同,mindmap 布局会结合节点实际尺寸进行排布(这一特点通过 getWidth / getHeight 配置项体现,详见下文)。

在 G6 中,mindmap 布局的实现来源于 @antv/hierarchy 库。从源码看,packages/g6/src/layouts/index.ts 直接导出了 compactBox、dendrogram、indented、mindmap 四个树布局,并统一在 packages/g6/src/registry/build-in.ts 的内置扩展注册表中以 mindmap: mindmap 注册为内置布局。因此在 G6 中直接使用 type: 'mindmap' 即可,无需额外注册。

在 G6 运行时中,mindmap 属于树布局(Tree Layout),packages/g6/src/utils/layout.ts 中的 isTreeLayout 将 'compact-box'、'mindmap'、'dendrogram'、'indented' 四种类型识别为树布局;packages/g6/src/runtime/layout.ts 中的 stepLayout 会为树布局走 treeLayout 分支:先通过 createTreeStructure 建立树结构,再对每棵树的根节点执行布局函数,最后将布局结果转换为 G6 的 LayoutMapping 格式回写节点坐标。

最小可用配置示例

在 Graph 构造函数的 layout 字段中配置即可:

const graph = new Graph({
  layout: {
    type: 'mindmap',
    direction: 'H',
    preLayout: false,
    getHeight: () => 32,
    getWidth: () => 32,
    getVGap: () => 16,
    getHGap: () => 72,
  },
});

type: 'mindmap' 是必须项;preLayout: false 表示不提前布局(关于 preLayout 的说明见下文“与运行时管线的配合”一节);其余四个 getXXX 均为尺寸/间距计算函数。

Options:配置项总览

下表汇总了 mindmap 布局的全部配置项:

Property Description Type Default Required
type 布局类型 mindmap - ✓
direction 布局方向,取值见下文 H | LR | RL | TB | BT LR
getHeight 计算每个节点高度的函数 (d?: Node) => number ✓
getWidth 计算每个节点宽度的函数 (d?: Node) => number ✓
getVGap 每个节点的垂直间距。注意:两个节点之间的实际垂直间距是该值的两倍 (d?: Node) => number
getHGap 每个节点的水平间距。注意:两个节点之间的实际水平间距是该值的两倍 (d?: Node) => number
getSide 设置节点放置在根节点左侧还是右侧。仅当 direction 为 H 时生效 (d?: Node) => string

需要特别提醒的是 getVGap / getHGap 的语义:传入的值会被当作半间距处理,两个节点之间的实际间距是设定值的 2 倍。例如设置 getHGap: () => 72,则相邻层级之间的实际水平距离为 144。这是在使用中容易踩坑的点,调整间距时请按“实际间距 = 设定值 × 2”来换算。

direction:五种布局方向详解

可选值:H | LR | RL | TB | BT,默认值为 'LR'。

树布局方向决定了子节点相对根节点的排布方位:

  • 'H'(水平/双侧):根节点的子节点被分为两部分,分别放置在根节点的左右两侧。可以通过 getSide 方法为每个节点指定左右分布逻辑;如果未提供 getSide,默认将前半部分子节点放在右侧,后半部分放在左侧。
  • 'LR'(左到右):子节点放置在根节点的右侧,形成典型的从左向右的树形展开;
  • 'TB'(上到下):与 'LR' 行为一致,子节点放置在根节点的右侧展开(即纵向深度方向上仍按层级纵向堆叠);
  • 'RL'(右到左):子节点放置在根节点的左侧,方向与 'LR' 相反;
  • 'BT'(下到上):子节点放置在根节点的右侧,随后整个图沿 X 轴旋转 180°,视觉上呈自下而上的生长方向。

注:仓库文档中 'LR' 与 'TB' 共用同一行为描述(子节点放在根节点右侧),在 G6 的 mindmap 布局中二者等价;需要真正上下方向生长的树,可参考同仓库的 DendrogramLayout 等其他树布局。

getWidth / getHeight:节点尺寸计算

两者签名均为 (d?: Node) => number,分别返回每个节点的宽度与高度。mindmap 布局会据此为节点预留空间,这也是本布局“考虑节点尺寸”的直接体现。配置时可以按节点 ID 差异化返回:

(d) => {
  // d 是一个节点
  if (d.id === 'testId') return 50;
  return 100;
};

在真实项目中,更常见的做法是让布局尺寸与节点的视觉尺寸保持一致,例如直接读取节点样式的 size。仓库测试用例 packages/g6/tests/demos/layout-mindmap-h.ts 中所有节点统一返回 16(配合节点 size: 26),说明该值只参与布局占位计算,不强制等于节点渲染尺寸——若节点实际渲染尺寸大于布局占位,节点之间可能发生视觉重叠,因此实践中建议让 getWidth / getHeight 的返回值与节点样式尺寸匹配。

getHGap / getVGap:间距计算

签名均为 (d?: Node) => number,分别计算每个节点的水平/垂直间距。注意实际间距为设定值的 2 倍(见前文):

(d) => {
  // d 是一个节点
  if (d.id === 'testId') return 50;
  return 100;
};

从测试用例可以看到典型取值:layout-mindmap-h.ts 中 getVGap: () => 10、getHGap: () => 50,即实际垂直间距 20、水平间距 100;layout-mindmap-h-left.ts 中 getHGap: () => 100,实际水平间距达到 200,用于更宽的树形展开。这些用例同时验证了当间距较大时,direction: 'H' 布局会把节点向两侧推开,配合 cubic-horizontal 边与左右两侧 ports,即可形成标准的思维导图视觉效果。

getSide:左右分侧逻辑(仅 H 方向生效)

签名:(d?: Node) => string。设置节点放置在根节点的左侧还是右侧,仅在 direction: 'H' 时生效。返回值约定为 'left' 或 'right'。若未设置,默认将前半部分子节点放在右侧,后半部分放在左侧。

(d) => {
  // d 是一个节点
  if (d.id === 'test-child-id') return 'right';
  return 'left';
};

从源码结构看,getSide 的自动计算逻辑由 @antv/hierarchy 的 separate-root 模块完成:未提供 getSide 时,布局会对根节点的子节点按顺序对半切分,前一半排布在右侧、后一半排布在左侧。因此当你需要“按业务语义”而不是“按顺序”决定节点方位时(例如血缘图中的上游/下游划分),务必显式提供 getSide。

仓库测试用例 layout-mindmap-h-custom-side.ts 演示了自定义分侧:

getSide: (d) => {
  if (d.id === 'Classification') {
    return 'left';
  }
  return 'right';
},

与之配套的单元测试 packages/g6/tests/unit/layouts/mindmap.spec.ts 覆盖了四种 H 方向场景:h(默认分侧)、h-left(全部在左)、h-right(全部在右)、h-custom-side(自定义分侧),均通过 SVG 快照断言布局结果。

与运行时管线的配合:preLayout 与树数据准备

mindmap 布局处理的是树结构数据。在 G6 中,通常先用 treeToGraphData 将树数据转换为图数据再交给 Graph,测试用例中的典型用法为:

import { Graph, treeToGraphData } from '@antv/g6';
import data from '@@/dataset/algorithm-category.json';

const graph = new Graph({
  data: treeToGraphData(data),
  layout: {
    type: 'mindmap',
    direction: 'H',
    // ...
  },
});

preLayout 控制布局执行的时机:

  • preLayout: false(默认):布局在元素渲染后执行,此时可读取元素的真实尺寸;
  • preLayout: true:在元素初始化前提前计算位置。从 packages/g6/src/runtime/layout.ts 的 getLayoutData 可以看出,preLayout 模式会跳过尚未渲染的元素(不要求元素已存在),并跳过隐藏、折叠祖先下的节点,适合需要提前确定位置的场景。

此外,运行时会为布局自动注入画布尺寸与中心点:initGraphLayout 中读取 viewport.getCanvasSize() 得到宽高并以 <a href="https://link.gitcode.com/i/92334177ed26a11f16f951887534104d" target="_blank">width / 2, height / 2] 作为布局中心(见 [packages/g6/src/runtime/layout.ts),因此 mindmap 布局默认会以画布中心为根节点位置展开,一般无需手动指定 center。

实战:基于 H 方向构建血缘图与思维导图

文档明确给出 mindmap 布局的两类典型适用场景:

1. 数据血缘图(Data lineage graph) 使用 direction='H' 渲染指定节点的上下游血缘:getSide 将上游节点归到左侧、下游节点归到右侧,中间节点作为根。例如:

layout: {
  type: 'mindmap',
  direction: 'H',
  getSide: (d) => (d.data.isUpstream ? 'left' : 'right'),
  // ...
},

2. 思维导图(Mind map) 基于 direction='H' 加上 getSide(或默认对半分侧)构建自定义思维导图组件。仓库中的完整案例 packages/g6/tests/demos/case-mindmap.ts 演示了如何把 mindmap 布局与自定义节点、collapse-expand 折叠展开、cubic-horizontal 水平曲线边组合成带配色的完整思维导图:根节点样式居中显示标签,子节点透明填充、按层级取色,并通过 ports 声明左右锚点让边从两侧引出。社区通用的“左右侧标签对齐”技巧在测试用例中也有体现(见 layout-mindmap-h.ts):比较节点 x 与根节点 x 的差,x >= 0 的节点标签放右侧、文字左对齐,否则放左侧、文字右对齐,保证标签始终朝外、不压住分支线。

小结

Mindmap 布局是 G6 内置的树布局之一,注册于 packages/g6/src/registry/build-in.ts,实现来自 @antv/hierarchy 的 mindmap 方法(packages/g6/src/layouts/index.ts)。配置要点可归纳为:

  • type 必填,direction 控制五种方向形态,默认 'LR';
  • getWidth / getHeight 让布局感知节点尺寸,建议与节点渲染尺寸保持一致;
  • getHGap / getVGap 以“半间距”语义生效,实际间距为设定值 2 倍;
  • getSide 仅对 H 方向有效,用于自定义左右分侧,是实现血缘图、复杂思维导图的关键;
  • 树数据需先经 treeToGraphData 转换,preLayout 决定布局相对渲染的执行时机。

更多同系列树布局可对比阅读 CompactBoxLayout、DendrogramLayout 与 IndentedLayout 文档。

登录后查看全文
G6