Your Site Name

从零到完美:Mermaid 时序图绘制工作流

2026年6年5日 · 1889

分类:工具

写在前面

最近在写一份技术文档,需要画架构图、时序图、流程图,加起来十几张。我比较抗拒用鼠标拖框——改个文字要重新对齐,换个节点要重画连线。

Mermaid 用代码生成图就好多了,改文字就改图。但问题来了:AI 生成的 Mermaid 代码渲染出来的 SVG,浏览器看着挺好,一插进 Word 就炸了。

矩形变黑块。线条消失。箭头方向乱跑。

花了一整天踩坑,最后把 beautiful-mermaid 这个开源库从里到外改了一遍,打包成了一个换台设备也能一键跑的工具包。这篇文章就是踩坑记录。

TIP: 如果你只是想在 Word 里插一张 Mermaid 图,直接跳到最后一节看工具包用法。

先看效果

这是同样的 Mermaid 源码,经过工具包渲染后的输出,直接拖进 Word 完美显示:

工具包渲染

IMPORTANT: 问题不在 Mermaid,也不在 beautiful-mermaid。问题在 Word 的 SVG 渲染能力——它不支持 CSS 变量 var(--bg),不支持 color-mix(),也不认 orient="auto-start-reverse"

第一关:文字全溢出了

刚开始渲染的第一批图,所有中文标签都溢出边框。

追了一下源码,beautiful-mermaid 估算文字宽度用的公式是 text.length × fontSize × 0.52。这个 0.52 是拉丁字符的平均宽高比。中文字符的宽高比接近 0.92——差了一倍。一个 5 字中文标签,估算宽度 34px,实际需要 60px。

修起来简单:数一下中文字符数,分别乘不同的系数。

// 原来:所有字符按拉丁算
return text.length * fontSize * widthRatio;

// 改后:中文单独算
const cjk = (text.match(/[一-鿿]/g) || []).length;
const latin = text.length - cjk;
return latin * fontSize * widthRatio + cjk * fontSize * 0.92;

修完这个,文字不溢出了。但图还是不对。

第二关:菱形又大又丑

流程图里的判断节点(菱形)用的是 side = max(width, height) + diamondExtra。原库的 diamondExtra: 24——额外加 24px。一个"资源负载评估"标签,文字宽 60px,加上 32px 内边距,再加上 24px 额外空间,菱形撑到 116×116。

旁边的矩形节点"接收任务"才 80×38。菱形比矩形大三倍,视觉上很突兀。

两刀修:diamondExtra 砍到 8,长标签加 <br/> 换行。"任务复杂度
评估"拆成两行后,菱形从 118px 缩到 100px。

<br/> 在 beautiful-mermaid 里默认不生效——escapeXml 会把 < 转成 &lt;。得让 escapeXml 保留 <br/>,然后把单行 <text> 改成多行 <tspan>

第三关:时序图缺胳膊少腿

Mermaid 的标准时序图有几个要素:参与者矩形、虚线生命线、激活框(覆盖在生命线上的细条)、箭头、文字。

beautiful-mermaid 只渲染了前两个+后两个。激活框没了。

原因是激活框只在 Mermaid 源码里显式标了 +/- 时才画。标准 Mermaid 不需要——自动从第一条消息到最后一条画激活框。

补了一段自动计算逻辑:每个参与者扫描自己的第一条和最后一条消息,中间全画激活框。白底、黑边框、居中在虚线上。

然后是箭头起止点。原版箭头从参与者中心出发,直接越过激活框到目标中心——箭头扎进框里了。改成从激活框右边缘出发,到目标激活框左边缘止。

自调用消息(自己发给自己的箭头)的箭头是折线,向右绕一圈回来。这个折线把激活框也带歪了——alt 框的宽度只算了参与者的宽度,没算自调用消息向右延伸的部分。修复方式是在框宽度计算里扫描框内每一条消息的标签位置。

第四关:Word 不认你的 CSS

前三关调完之后,浏览器里看着已经很好了。插进 Word——全崩。

F12 看了一下 SVG 源码,beautiful-mermaid 用 CSS 变量实现主题:

<rect fill="var(--_node-fill)" stroke="var(--_node-stroke)" ... />

--_node-fill 的定义在 <style> 里:var(--surface, color-mix(in srgb, var(--fg) 3%, var(--bg)))

浏览器能解这个链条,Word 不能。Word 看到 var(--_node-fill) 不认识,默认给黑色——所以矩形全黑了。color-mix() 不认识,直接跳过——所以线条消失了。

解决方案:写了一个 flatten-svg.js,把 SVG 里所有 CSS 变量替换为具体色值。github-white 主题的配色很简单:

变量
--bg / --surface#FFFFFF
--line / --border / --accent#000000
--fg#1f2328
--muted#555555

color-mix(in srgb, var(--fg) 20%, var(--bg)) 这种相对值也直接算成 #d2d3d4

还把 <style> 块整个删了——Word 不需要那些 CSS 定义,只要元素上的颜色属性是具体的。

另外发现 Word 不支持 SVG 的 dy 属性(相对于基线的偏移量)。文本位置原本是 <text y="100" dy="0.35em">,Word 忽略 dy,文字就偏上了。改成 <text y="105">——把偏移直接算进 y 坐标,问题解决。

第五关:箭头的方向——坑最深的

颜色问题修完之后,插进 Word 一看——返回箭头全朝右了。

时序图里,实线箭头(请求)朝右,虚线箭头(返回)朝左。但 Word 里所有箭头都朝右。

问题出在 SVG 的 <marker> 标签。beautiful-mermaid 给 marker 设了 orient="auto-start-reverse"——浏览器会根据线条方向自动把 marker 翻转 180 度。Word 不认这个属性,所有箭头按原始多边形方向渲染,全朝右。

试了三版。

第一版:把 auto-start-reverse 换成 auto。Word 还是不认。

第二版:创建两个 marker——-l(左指)和 -r(右指),根据线条 x1 > x2 判断往哪走。方向对了,但位置漂了——箭头尖没对齐线端点。排查发现我只翻转了多边形的顶点坐标,没改 refX(连接点)。左指箭头的尖在 x=0 但 refX 还指着 x=10。

第三版:翻转多边形的时候同步翻转 refX。左指箭头:多边形镜像 + refX 归零。右指箭头:原样保留。

// 左指 marker: 多边形水平翻转 + refX 归零
const flipped = polygon.replace(...); // x -> w-x
const lMarker = marker.replace('refX="10"', 'refX="0"').replace(polygon, flipped);
// 右指 marker: 原样
const rMarker = marker;

WARNING: auto-start-reverse 只影响 sequence 图的 seq-arrowseq-arrow-open marker。流程图的 arrowhead 用的是 orient="auto" 不需要处理。还有个大坑:正则匹配 marker 时如果用 [\s\S]*? 会跨过前面的 <marker> 标签,把相邻的 arrowhead 也吞进去复制一份——结果变成两个 id="arrowhead",SVG 非法,Word 直接不渲染箭头。改成 <marker\b[^>]*\bid="..." 匹配单个标签才修好。

工具包

所有修改已经打包。换台设备:

git clone https://github.com/Raylene-Y/mermaid-toolkit.git && cd mermaid-toolkit
bash install.sh

然后一行命令:

node render-custom.mjs -i diagram.mmd -o diagram.svg -t github-white
node flatten-svg.js diagram.svg

diagram-flat.svg 直接拖进 Word——白底黑线,箭头方向正确,菱形大小合理,激活框正常。

项目地址:github.com/Raylene-Y/mermaid-toolkit

总结

一天时间改了 18 处。核心教训就三条:

  1. 英文开源库对中文的支持几乎为零。文字宽度、换行、标记方向,每个环节都要手动适配。
  2. Word 的 SVG 支持远比你想象的弱。CSS 变量、color-mix()auto-start-reverse 全不支持。但它的 SVG 规范支持其实还行——只要把颜色写死、marker 拆好,就能完美渲染。
  3. flatten-svg.js 打通了最后一公里。原来是 beautiful-mermaid → 浏览器能看但 Word 不能看,现在加一层压平,输出直接进 Word。

如果有和我一样需要在 Word 文档里大量插 Mermaid 图的,这个工具包应该能省你一天时间。