首页
/ Mermaid Treemap(矩形树图)语法与配置完整指南:从缩进层级到 D3 数值格式化

Mermaid Treemap(矩形树图)语法与配置完整指南:从缩进层级到 D3 数值格式化

2026-09-06 18:57:17作者:傅爽业Veleda

Treemap(矩形树图)是 Mermaid 内置的一种层次化数据可视化图,它把层级结构映射为一组嵌套矩形,每个分支对应一个矩形,再用更小的矩形在其内部“平铺”出子分支,矩形面积正比于其所代表的数值,适合在有限空间内比较同一层级的占比与大小。本文以仓库内 treemap 语法文档 为骨架,结合其解析器、数据库与渲染器的真实实现,完整讲解从语法编写、样式定制、主题与配置参数到值格式化、自动字号收缩与面积布局原理的全部细节。读完本文,你将能直接写出可运行的分层/扁平 Treemap 图,并精确控制配色、内边距、数值显示格式与适配缩放行为。

Treemap 是什么,适合展示什么

官方定义:Treemap 图把层次化数据展示为一组嵌套矩形。树中的每个分支由矩形表示,随后在该矩形内部用更小的矩形铺满表示其子分支。

packages/mermaid/src/diagrams/treemap/types.ts 中的数据模型也印证了这一结构:TreemapNode 同时具备 value(叶子数值)与 children(子节点数组),并带有用于类样式选择的 classSelector 与编译后的 cssCompiledStyles

Treemap 尤其适用于:

  • 可视化树状/层次化数据结构;
  • 比较不同类别之间的占比关系(面积即比例);
  • 在有限画布中展示大量层级数据;
  • 快速识别层级数据中的模式与离群点。

仓库根目录 demos/treemap.html 就是可直接在浏览器中加载的运行示例,docs/syntax/ 下还维护了一份面向站点的同步文档 treemap.md,可作为验证语法与效果的第一手环境。

语法入门与基础结构

Treemap 图以 treemap-beta 关键字开头(解析器同样接受 treemap),随后以“缩进”表达父子关系。最简单的入门示例:

treemap-beta
"Section 1"
    "Leaf 1.1": 12
    "Section 1.2"
      "Leaf 1.2.1": 12
"Section 2"
    "Leaf 2.1": 20
    "Leaf 2.2": 25

节点定义规则

节点可归纳为两种角色、三类写法:

  • Section / 父节点:用带引号的文本定义,如 "Section Name",本身可有值也可无值,起分组与嵌套作用;
  • 带值的 Leaf 叶子节点:带引号文本后跟冒号和数值,如 "Leaf Name": value
  • 层级:完全靠缩进建立——空格或制表符均可,同一节点的子节点缩进越深,层级越深(每层一级缩进即可,无需统一为固定空格数)。

需要注意,从语法的实际定义看(见下文 Langium 文法一节),叶子节点的数值分隔符既可以是冒号 : 也可以是逗号 ,,分隔符两侧允许有空格。缩进层级本身由顶层解析后转换为数字 level 记录,再在 parser.ts 中通过 buildHierarchy 把“扁平行数组”装配成嵌套树。

从三个完整示例快速上手

以下三个示例可直接复制到 demos/treemap.html 或任意 Mermaid 渲染器(Mermaid Live Editor、支持 Mermaid 的 Markdown 预览等)中运行。

基础 Treemap(单层分类对比)

treemap-beta
"Category A"
    "Item A1": 10
    "Item A2": 20
"Category B"
    "Item B1": 15
    "Item B2": 25

这是最简单的“分类-明细”两层结构:Category A/B 是顶层 Section,每个 Item 是带值的叶子;矩形面积即随 Item 数值等比放大,肉眼即可比较 A、B 两个类别内部及之间的相对大小。

层次化 Treemap(多层嵌套)

treemap-beta
"Products"
    "Electronics"
        "Phones": 50
        "Computers": 30
        "Accessories": 20
    "Clothing"
        "Men's": 40
        "Women's": 40

这里嵌套了三级:顶层 Products → 中层品类 → 底层商品明细。渲染器对每一层“分支节点”都会绘制带标题栏的区域(Section 头),从而把“分组→子分组→明细”的归属关系清晰地表达出来。

带样式(class)的 Treemap

treemap-beta
"Section 1"
    "Leaf 1.1": 12
    "Section 1.2":::class1
      "Leaf 1.2.1": 12
"Section 2"
    "Leaf 2.1": 20:::class1
    "Leaf 2.2": 25
    "Leaf 2.3": 12

classDef class1 fill:red,color:blue,stroke:#FFD600;

样式语法与 Mermaid 其他图类型一致:节点名后用 :::className 追加类名,文件末尾用 classDef className style1,style2; 声明类的视觉属性(本节中的 fillcolorstroke 均为 CSS 属性值)。Section 与 Leaf 节点都可以挂类,也可以把同一个类同时挂到多个节点上。

Styling 与 Configuration:样式定制

用 classDef 精细控制外观

classDef 是 Mermaid 多图共享的标准样式机制,Treemap 完整支持:

treemap-beta
"Main"
    "A": 20
    "B":::important
        "B1": 10
        "B2": 15
    "C": 5

classDef important fill:#f96,stroke:#333,stroke-width:2px;

从实现看,TreeMapDB.addClass 会在 db.ts 中把 classDef 的样式字符串先做一次关键转换——用占位符保护 \,(转义逗号),然后把普通逗号 , 替换为 ; 再按 ; 拆分,逐个入列并区分 textStyles(文字类样式,如 colorfont-size)与节点 styles(如 fillstroke)。渲染阶段(renderer.ts)会调用 styles2String 把编译后的样式合并进对应矩形的 style 属性,并把 color: 改写为 fill: 以适配 SVG 文本着色。

主题配置

Treemap 的颜色体系全部走 Mermaid 主题变量,因此切换内置主题即可整体换肤。用文档头(frontmatter)配置主题是最直接的方式:

---
config:
    theme: 'forest'
---
treemap-beta
"Category A"
    "Item A1": 10
    "Item A2": 20
"Category B"
    "Item B1": 15
    "Item B2": 25

theme 的可选值与 Mermaid 全局一致(如 defaultneutraldarkforest 等)。从渲染器源码可以看到主题是如何参与配色的:颜色来自 themeVariables.cScale0~cScale11(Section 填充色)、cScalePeer0~cScalePeer11(描边色)与 cScaleLabel0~cScaleLabel11(文字色)三组有序色标,采用 scaleOrdinal 依名称映射到不同分块,从而让同层相邻区块自动分色。叶子节点遵循“继承最近父 Section 的颜色”规则(见渲染器中 d.parent ? colorScale(d.parent.data.name) : colorScale(d.data.name)),保证同一分组下的叶子观感一致。

外边距 diagramPadding

可通过 diagramPadding 调整整个图四周留白:

---
config:
  treemap:
    diagramPadding: 200
---
treemap-beta
"Category A"
    "Item A1": 10
    "Item A2": 20
"Category B"
    "Item B1": 15
    "Item B2": 25

这里 diagramPadding 作用于整幅图的 viewport 留白(在渲染结尾由 setupViewPortForSVG(svg, diagramPadding, ...) 消费),与下文将介绍的内部 padding(节点间的内间距)作用域不同,注意区分。

配置参数全表与源码默认值

Treemap 图支持以下配置项(均需放在 config.treemap 命名空间下,或经 frontmatter 直接配置):

Option 说明 默认值
useMaxWidth 为 true 时图宽设为 100%,随容器可用宽度缩放 true
padding 节点(矩形块)之间的内部间距 10
diagramPadding 整幅图四周的外边距 8
showValues 是否在节点上显示数值 true
nodeWidth 节点宽度 100
nodeHeight 节点高度 40
borderWidth 边框宽度 1
valueFontSize 数值字号 12
labelFontSize 标签字号 14
valueFormat 数值显示格式,详见下文“值格式化” ','

这些默认值并不是文档里凭空写的——defaultConfig.tstreemap 段的默认值与上表完全一一对应,TreemapDB.getConfig() 使用 cleanAndMerge 将默认配置与用户传入的 config.treemap 做深合并后返回(合并实现见 db.ts)。配置接口 TreemapDiagramConfig 亦在 types.ts 中定义,参数类型均为数值或布尔/字符串。

上述表中部分参数在渲染器中还有“兜底常量”作为第二道防线,例如内部间距若未配置会落到 DEFAULT_INNER_PADDING = 10;Section 标题栏固定高度为 SECTION_HEADER_HEIGHT = 25,Section 区域四周的 SECTION_INNER_PADDING 默认也取 10。

渲染管线:从文本到嵌套矩形

了解语法与配置后,再看 Treemap 的实现链路会更有把握调优:

  1. 类型探测:加载入口 detector.ts 用正则 /^\s*treemap/ 判断文本块是否属于该图类型,命中后异步加载 diagram.js,这也是 Treemap 作为可延迟加载图类型的体现。
  2. 语法解析:文本交给 @mermaid-js/parser 包的 Langium 文法解析成 AST。文法文件在 treemap.langium,关键点包括:开始关键字 terminal TREEMAP_KEYWORD: 'treemap-beta' | 'treemap';节点名 terminal STRING2 支持双引号或单引号包裹的字符串;叶子行规则为 name=STRING2 INDENTATION? (SEPARATOR|COMMA) INDENTATION? value=MyNumber (STYLE_SEPARATOR classSelector=ID2)?,即分隔符支持 :,classDef:::class 分别由 CLASS_DEFSTYLE_SEPARATOR terminal 处理;缩进由 INDENTATION(一或多个空格/制表符)捕获,%% 注释、换行等作为隐藏 token 被忽略。文法头部注释也注明其由 mindmap 文法改造而来。
  3. 数据装配parser.ts 先遍历 AST 提取 ClassDefStatement 注册到类表,再把每行(含缩进层级、名称、值、类选择器)拍平为 items,随后调用 buildHierarchy 依据缩进层级关系把它们组织成嵌套树,递归 db.addNode 入库;level === 0 的顶层节点会同时登记为 outerNodes 与首个 root。
  4. 布局与绘制renderer.ts 中,数据先经 hierarchy(root).sum(value).sort(降序) 计算每个子树聚合值并按值降序排序,再交给 D3 的 treemap() 布局:size 取画布宽高,paddingTop 对含子节点的 Section 额外加上标题栏高度与内间距,paddingInner 取配置的 padding。随后分支节点(有 children 者)被画成带标题栏的 Section 块,叶子节点被画成填充矩形,最后统一叠加标题、标签与数值文本。
  5. 标题与可访问性titleaccTitleaccDescr 指令同样受支持(文法中的 TitleAndAccessibilities 规则),与 Mermaid 其他图一致。

Advanced:值格式化(valueFormat)

valueFormat 主要使用 D3 的格式说明符(d3-format)控制数字显示,并对常见货币格式做了额外的特殊兼容处理。渲染器中的格式化函数构造逻辑为:当 formatStr === '$0,0' 时走“美元符号 + 千分位”分支;以 $ 开头且含逗号时提取小数精度后拼接 D3 的 ',' + precision 格式;仅以 $ 开头则剥掉前缀再把余下部分交给 D3;否则全部按标准 D3 format() 处理;一旦 format() 抛错(非法格式串)会回退到默认千分位格式 ','

常用格式速查:

  • , — 千分位分隔(默认);
  • $ — 加美元符号前缀;
  • .1f — 保留 1 位小数;
  • .1% — 以百分比显示并保留 1 位小数;
  • $0,0 — 美元符号 + 千分位;
  • $.2f — 美元符号 + 2 位小数;
  • $,.2f — 美元符号 + 千分位 + 2 位小数。

货币格式示例(适合预算类数据):

---
config:
  treemap:
    valueFormat: '$0,0'
---
treemap-beta
"Budget"
    "Operations"
        "Salaries": 700000
        "Equipment": 200000
        "Supplies": 100000
    "Marketing"
        "Advertising": 400000
        "Events": 100000

百分比格式示例(适合市场份额类数据,值写小数即可):

---
config:
  treemap:
    valueFormat: '$.1%'
---
treemap-beta
"Market Share"
    "Company A": 0.35
    "Company B": 0.25
    "Company C": 0.15
    "Others": 0.25

面积、字号与标签的自动适配细节

Treemap 的矩形面积本身由 D3 布局依据数值自动分配,但文案是否完整可读还取决于单元格大小。源码对叶子文本的处理值得注意(这也是影响体验的隐藏逻辑):

  • 当叶子数量超过 20(leafNodes.length > 20)时判定为“复杂图”,标签/数值的基础字号与最小字号会整体下调(例如标签基础字号由 38px 降为 16px),反之使用更大字号,确保小格子里不互相遮挡;
  • 每个叶子都会创建 clipPath 防止文字溢出;
  • 渲染后会对每个标签做动态字号收缩:先按可用宽度逐步减小字号直到放得下,再把“标签 + 数值”的预估组合高度与单元格高度比较,必要时继续缩减并同步缩放数值字号(数值字号默认取标签字号的 0.6 倍);
  • 当单元格太小(可用宽/高低于阈值)或字号已缩到下限仍放不下时,标签与数值文本会被 display: none 隐藏,保证图形本身不被挤坏;
  • 数值文本只有在标签可见时才会尝试绘制,其纵坐标动态定位在标签下方,并校验是否超出单元格底部(底部留 4px)。

因此当看到极小的格子“没有文字”时,通常不是数据缺失,而是自动适配机制的合理取舍。

典型使用场景

结合矩形面积编码的特性,Treemap 常见于以下五类数据:

  1. 财务数据:预算分配、市场份额、投资组合构成的可视化;
  2. 文件系统分析:按文件夹与文件展示磁盘占用分布;
  3. 人口统计:跨地区及子地区展示人口分布;
  4. 产品层级:展示产品品类及其销量规模;
  5. 组织结构:表示公司部门与团队人数规模。

从仓库自带的测试集也能看到官方对其应用形态的覆盖,例如 e2e/diagrams/treemap/ 下包含带样式的叶子、Section 边框描边、超长名称省略号、隐藏数值、大数值千分位格式化等回归用例,可作为进一步查阅“该特性到底长什么样”的实证素材。

局限与注意事项

  • Treemap 最适合具有自然层级结构的数据,扁平无层级的数据表现力较弱;
  • 极小数值对应的矩形会很难看清或放不下标签;
  • 过深的多层层级难以在有限面积内表达清楚;
  • Treemap 不适合展示含负值的数据(面积无法表达负数,布局与聚合逻辑会受影响)。

备选图型参考

若 Treemap 不贴合你的需求,可考虑 Mermaid 中的相近替代(相关文档均位于 docs/syntax):

  • Pie Chart(饼图):无层级、仅做简单占比比较时更直观;
  • Sunburst(旭日图):径向布局的层级数据(Mermaid 尚未正式发布);
  • Sankey(桑基图):适合表达基于“流量/流向”的层级数据。

Notes:关于 beta 状态

Mermaid 官方在文档中明确提示,Treemap 是较新的图类型,语法可能在未来版本中演进。其实现设计目标是“上手简单但能力完整”,若在使用中发现语法或渲染问题,欢迎通过 Mermaid 的 issue 反馈。如果你想在自己的项目里即时体验,直接打开 demos/treemap.html 即可在浏览器中边改边看。

登录后查看全文
热门项目推荐
相关项目推荐