D2 v0.1.4 交互式图表:tooltip 与 link 关键字、附录渲染与布局引擎选项详解
2026/9/12 0:10:33 网站建设 项目流程

D2 v0.1.4 交互式图表:tooltip 与 link 关键字、附录渲染与布局引擎选项详解

【免费下载链接】d2D2 is a modern diagram scripting language that turns text to diagrams.项目地址: https://gitcode.com/GitHub_Trending/d2/d2

D2 是一门把文本描述编译成图表的现代图表脚本语言。v0.1.4 是 D2 发展历程中里程碑式的一个版本:它首次引入交互式图表能力——图形可以挂载tooltip(悬停提示)与link(点击跳转)属性;同时把此前仅限图片的width/height关键字开放给所有非容器图形,并全面暴露各布局引擎的可配置选项。本文以该版本的官方变更日志(ci/release/changelogs/v0.1.4.md)为主线,结合当前仓库源码,逐一拆解这些能力的用法、底层实现与升级注意事项,读完即可在自己的 D2 脚本和库调用中直接落地这些新特性。

一、交互式图表:让静态图形“动”起来

v0.1.4 的核心卖点是交互式图表:D2 图形现在可以设置tooltiplink。其中:

  • tooltip允许读者把鼠标悬停在图形上查看更多信息;
  • link允许读者点击图形跳转到外部链接。

这一个小改动打开了巨大的想象空间——例如把企业内部 Wiki 通过图表互相串联,形成可点击导航的知识图谱。当某个图形带有可悬停的 tooltip 或可点击的链接时,D2 会在图形上渲染一个图标作为提示。

1.1 关键字在语法层面的定位

在 D2 语法树中,tooltiplink被定义为一等保留关键字。查看 d2ast/keywords.go 可以看到它们同时出现在两个集合中:

  • SimpleReservedKeywords"tooltip""link"等):它们可以直接以key: value的形式附着在图形上;
  • CompositeReservedKeywords中的"tooltip":意味着 tooltip 还可以承载复合对象(例如多行文本内容)。
server: { tooltip: "生产环境主节点,承载订单服务" link: https://wiki.example.com/services/order } database: { label: "订单数据库" tooltip: | 只读副本 3 个 主从延迟 < 500ms link: https://grafana.example.com/d/orders }

1.2 从源码看 tooltip/link 的数据结构

交互属性最终会被编译进目标图(target diagram)的 Shape 结构中。在 d2target/d2target.go 中可以看到 Shape 上挂着三个相关字段:

Tooltip string `json:"tooltip"` Link string `json:"link"` PrettyLink string `json:"prettyLink,omitempty"`
  • Tooltip是悬停时展示的原始文本(支持 Markdown);
  • Link是点击跳转的完整 URL;
  • PrettyLink是链接的展示文本,用于在非交互场景下“尽量美观地”呈现这个链接,因为静态导出格式无法点击。

而 tooltip 的展示位置由TooltipPosition字段控制(d2target/d2target.go)。合法取值在 d2ast/keywords.go 的TooltipPositionsArray中定义:

var TooltipPositionsArray = []string{ "top-left", "top-center", "top-right", "center-left", "center-right", "bottom-left", "bottom-center", "bottom-right", }

在 SVG 等交互格式中,D2 会为带位置的 tooltip 生成一个可见的气泡框:在 d2renderers/d2scenebuild/tooltip.go 中,buildPositionedTooltip会先按 Markdown 排版 tooltip 内容,再根据TooltipPosition计算气泡的坐标(调用d2target.CalculateTooltipPosition),最后绘制圆角背景框、指向图形的尾巴(tail)以及 Markdown 文本内容,并把这些节点追加到所有图形之后以保证它们始终处于最上层。

二、静态导出格式的附录(Appendix)机制

交互特性在 PNG 这类静态导出格式上显然无法工作。因此 v0.1.4 规定:当导出到静态格式时,tooltip 与 link 会被自动收录进一个“附录”(appendix)中——每个交互属性对应一个带编号的图标,正文下方多出一块编号清单,读者虽然无法悬停点击,但依然能看到每条元数据。

2.1 附录的源码实现

附录逻辑集中在 d2renderers/d2scenebuild/appendix.go 中,核心流程分三步:

  1. 收集(addAppendixItem)addAppendixItem记录每个无法交互呈现的元数据(连接线的 tooltip、Markdown 链接标题等),并为附录项数量与字符串总字节数设置预算上限(受LinkBudget约束);
  2. 测量(measureAppendix):用文本测量工具计算每一行的宽高,逐步累加出附录整体占用的高度,并通过expandViewBox在导出时把视口(viewBox)向下扩展;
  3. 绘制(buildAppendix):在正文下方先画一条分隔线,然后为每一条附录渲染“编号圆形图标 + 文本”的行。

对应地,d2renderers/d2svg/appendix/appendix.go 负责在 SVG 序列化阶段把附录真正写进输出文档。

2.2 附录图标的位置计算

对于正文中的图形,D2 会在图形右上角绘制白色圆形编号徽标(tooltip 与 link 同时存在时是两个并排徽标)。appendixIconCenters(appendix.go)会根据图形几何类型做特判:例如圆形、椭圆、菱形、人形、云、圆柱等图形在同时有两个徽标时会把链接徽标挪到图形内部;step、hexagon、queue、page 等形状则把徽标对齐到顶部。

例如一个同时带 tooltip 和 link 的矩形,导出 PNG 后会形如:

service: "订单服务" { tooltip: "点击查看监控面板" link: https://grafana.example.com/d/orders }

导出 PNG 后,图形右上角会出现① ②两个徽标,图下方附录区则列出:

① 订单服务 tooltip: 点击查看监控面板 ② 订单服务 link: https://grafana.example.com/d/orders

三、width 与 height:不再只是图片的专属

在 v0.1.4 之前,widthheight是 D2 关键字,但只对图片(icon)生效。本次更新把这两个关键字开放给了所有非容器图形

box: { width: 200 height: 100 label: "固定尺寸的矩形" } person: "固定尺寸的人形" { width: 120 height: 120 }

也就是说,任意非容器形状(矩形、椭圆、人形、圆柱等)现在都可以通过width/height显式控制自身尺寸,这让精确排版成为可能。注意容器(container,即内部还有子图形的形状)仍然不适用这两个关键字,因为它们的大小由内部内容决定。

这一改动与同版本的另一项修复互为表里——图形边框宽度(stroke width)被纳入图表整体包围盒(bounding box)的计算,详见下文 Bugfix 部分。

四、布局引擎选项全面开放

v0.1.4 还给了用户“更多控制布局的权力”:所有布局引擎的配置项都被暴露出来。D2 在无任何输入的情况下会为每个布局引擎设置合理的默认值,因此这些配置属于面向高级用户的特性——需要额外控制时才使用。

4.1 dagre 布局引擎的可配置选项

dagre 是 D2 默认的布局引擎,其配置结构定义在 d2layouts/d2dagrelayout/layout.go:

type ConfigurableOpts struct { NodeSep int `json:"nodesep"` EdgeSep int `json:"edgesep"` } var DefaultOpts = ConfigurableOpts{ NodeSep: 60, EdgeSep: 20, }
选项默认值含义
NodeSep60同一层级内相邻节点之间的间距(水平方向)
EdgeSep20边与边之间的间距(水平方向)

4.2 ELK 布局引擎的可配置选项

ELK 布局引擎的配置结构定义在 d2layouts/d2elklayout/layout.go:

type ConfigurableOpts struct { Algorithm string `json:"elk.algorithm,omitempty"` NodeSpacing int `json:"spacing.nodeNodeBetweenLayers,omitempty"` Padding string `json:"elk.padding,omitempty"` EdgeNodeSpacing int `json:"spacing.edgeNodeBetweenLayers,omitempty"` SelfLoopSpacing int `json:"elk.spacing.nodeSelfLoop"` } var DefaultOpts = ConfigurableOpts{ Algorithm: "layered", NodeSpacing: 70.0, Padding: "[top=50,left=50,bottom=50,right=50]", EdgeNodeSpacing: 40.0, SelfLoopSpacing: 50.0, }
选项默认值含义
Algorithm"layered"ELK 布局算法,默认分层布局
NodeSpacing70层内节点间距
Padding"[top=50,left=50,bottom=50,right=50]"容器内边距
EdgeNodeSpacing40边与节点之间的间距
SelfLoopSpacing50自环(self-loop)边的间距

从源码看,这些用户选项会与引擎内部固定参数合并:newRootLayoutOptions(layout.go)在根容器上固定Thoroughness: 8EdgeEdgeBetweenLayersSpacing: 50HierarchyHandling: "INCLUDE_CHILDREN"CycleBreakingStrategy: "GREEDY_MODEL_ORDER"等 ELK 原生参数;而newContainerLayoutOptions(layout.go)在子容器上会关闭 model-order 处理——源码注释明确说明这是为了避免 ELK 0.12 在复合子图上崩溃(这也与 v0.1.4 修复的 ELK panic 类问题一脉相承)。此外,若用户未显式设置SelfLoopSpacing,布局器会根据图中自环标签的最大尺寸动态放大该间距,防止自环标签互相重叠(layout.go)。

4.3 库调用方式的 Breaking Change

把布局选项暴露出来的代价是一个破坏性变更:当你把 D2 当作 Go 库使用时,d2dagrelayout.Layoutd2elklayout.Layout现在都接受第三个参数(options)。如果希望保持默认行为,请改用各自包下的DefaultLayout

// 旧写法(v0.1.4 之前) // import "github.com/d2lang/d2/d2layouts/d2dagrelayout" // err := d2dagrelayout.Layout(ctx, graph) // 新写法(v0.1.4 起) import "github.com/d2lang/d2/d2layouts/d2dagrelayout" // 方式一:使用默认选项(推荐) err := d2dagrelayout.DefaultLayout(ctx, graph) // 方式二:自定义选项 opts := d2dagrelayout.ConfigurableOpts{ NodeSep: 80, EdgeSep: 30, } err := d2dagrelayout.Layout(ctx, graph, &opts)

ELK 同理:

import "github.com/d2lang/d2/d2layouts/d2elklayout" // 默认 err := d2elklayout.DefaultLayout(ctx, graph) // 自定义 opts := d2elklayout.ConfigurableOpts{ Algorithm: "layered", NodeSpacing: 90, Padding: "[top=60,left=60,bottom=60,right=60]", EdgeNodeSpacing: 50, SelfLoopSpacing: 60, } err := d2elklayout.Layout(ctx, graph, &opts)

从实现看,DefaultLayout内部就是Layout(ctx, g, nil)——传入 nil 时布局器会回退到包级DefaultOpts(d2dagrelayout/layout.go、d2elklayout/layout.go),因此两种写法语义完全一致。

五、改进与 Bugfix 一览

5.1 改进:Watch 模式自适应缩放

v0.1.4 起,watch 模式(d2 --watch)下渲染内容会自动适配屏幕,不再出现图形超出可视区域需要手动滚动/缩放的尴尬。这对使用 watch 模式做交互式编辑的用户是实打实的体验提升。

5.2 本次修复的 Bug

问题说明
classtable空表头渲染错误修复了类图与表格在没有表头时的渲染问题
sql_table无列时渲染错误修复了 SQL 表不包含任何列时的渲染问题
包围盒未计入描边宽度图表整体 bounding box 现在会为 stroke width 预留空间,避免描边被裁切
near关键字位置不受控near: top-center这类常量此前在容器上会导致布局错乱或直接报错;现在限制near常量只能在合法位置使用,并给出友好错误信息而非随机报错。相关合法取值定义在 d2ast/keywords.go 的NearConstantsArray中(top-leftbottom-right共 8 个)
ELK 渲染空标签图片时 panic修复了带空标签的图片走 ELK 布局时程序崩溃的问题

需要说明的是:由于near关键字值必须是合法的 D2 关键字(其常量集合见 d2ast/keywords.go),在容器上使用这些常量会破坏容器内子图形的约束,因此 v0.1.4 在编译期就拦截这类写法并输出清晰的错误提示。

六、升级到 v0.1.4 的注意事项

综合以上内容,从旧版本升级到 v0.1.4 时需要注意:

  1. 库调用签名变化(必须处理):如果你的项目直接以 Go 库方式调用d2dagrelayout.Layoutd2elklayout.Layout,需要为调用补上第三个参数,或直接改用DefaultLayout保持默认行为(见 4.3 节示例)。
  2. near使用受限:如果你之前曾在容器上使用near: top-center之类的写法,请改为在合法对象上使用,否则会得到编译错误(这是刻意为之,为的是提供确定性行为与友好报错)。
  3. 静态导出会附带附录:导出 PNG/GIF/PDF/PPTX 等静态格式时,所有 tooltip 与 link 都会自动生成附录行与编号徽标,这是预期行为而非缺陷;若完全不需要这些元数据,就不要在脚本中写tooltip/link

结语

v0.1.4 通过tooltip/link把 D2 从“只能看”的静态图表推进到“可以悬停、可以点击”的交互式图表,同时借助附录机制保证了静态导出场景下信息不丢失;width/height的开放与布局引擎选项的暴露,则让高级用户获得了精确排版与深度控制布局的能力。这一版本确立的架构——关键字在 d2ast/keywords.go 中定义、交互数据承载于 d2target/d2target.go 的 Shape 结构、静态附录渲染在 d2renderers/d2scenebuild/appendix.go 与 d2renderers/d2svg/appendix/appendix.go 中实现——至今仍是 D2 交互与静态导出两大能力的基础。如果你正在做企业内部文档体系或需要精确控制版式的图表,这个版本引入的能力值得直接上手。

【免费下载链接】d2D2 is a modern diagram scripting language that turns text to diagrams.项目地址: https://gitcode.com/GitHub_Trending/d2/d2

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询