拓冰建站拓冰建站
首页 / 资讯中心 / 正文

PlantUML现代化主题美化指南:从默认风格到优雅文档

如果你用过PlantUML应该会有同感生成的图“能用”但离“好看”总差一口气。类图边框是粗黑的、节点背景带着荧光蓝、箭头硬邦邦地拐来拐去放进博客或技术文档里一眼就能看出是工具自动生成的跟周围的排版完全不搭。这篇就是我折腾PlantUML画图风格的一次完整记录——从默认主题的审美问题出发把样式控制机制梳理清楚然后给出一套可以直接复用的现代化主题源码以及我在真实项目里踩过的坑和排查思路。适合不想在“画图工具”上花太多时间但又希望图在文档里拿得出手的朋友。1. 默认风格难看的真正原因一份“审美诊断”1.1 颜色体系拉不开层次PlantUML默认主题最让人不舒服的地方不是某一个元素丑而是整张图的视觉层次是乱的。默认配色里类节点背景是一种明度很高的蓝色或黄色边框又是深色粗线包package的背景色和注记note的颜色相近一眼看过去满屏都是高饱和色块观众的注意力会被均匀地分散到每一个元素上根本分不清主次关系。这个问题在单张图里可能还不明显但当你用它做架构图、画类关系图并放进设计文档时致命伤就暴露了颜色没有语义。所有节点看起来都是一个强度读者必须逐一阅读文字才能理解结构这完全违背了“一张图说明白一个系统”的初衷。真正的美观不是颜色多而是颜色之间有主次、有层级、有情绪。默认配色恰恰把这一点做反了。这也是为什么网上很多教程建议“把背景改成白色”“把边框改细”之后图看起来还是不对——因为问题的根源不在某一个具体参数而在于整套颜色体系缺乏一致性的设计逻辑。1.2 布局与线条缺乏细节PlantUML默认生成的图节点之间的连线通常是纯黑色或者非常粗的黑色折线。从设计角度看黑色是最强对比色当图中连线很多时黑线混杂在一起视觉噪音会非常大。另一个问题是默认边框普遍偏粗。PlantUML里很多默认主题的边框宽度大约在1到2像素左右看起来已经比普通网页设计里的hairline0.5px到1px粗很多。再加上默认的直角折线整个图会呈现一种“老式软件架构图”的既视感——实用主义有余精致度不足。还有阴影。默认主题里节点是有阴影的这让图形有一点立体感但也让整张图蒙上一层“微软Office 2003时代”的观感。现代设计语言里扁平化、无阴影、细边框、大圆角、留白充足才是更耐看的趋势。想让PlantUML图显得专业去阴影、改细线、控制圆角是第一步。1.3 “越堆越丑”是更常见的灾难我在很多开源项目里见过另一种情形作者意识到默认主题不好看于是开始手工调参数把类背景改成五颜六色的渐变色把箭头颜色改成红色、绿色、橙色每个节点都不一样。结果就是颜色越多图越乱。这种“越堆越丑”的本质问题在于作者把“美化”理解成了“给每个元素加不一样的样式”。真正专业的排版逻辑恰恰相反——全局先统一局部再突出重点。一个节点需要被高亮前提是其他节点都保持低调如果所有节点都在抢眼球突出就无从谈起。所以美化PlantUML的核心不是会写多少条skinparam而是先建立起一套克制的、有层次的视觉规则再让每个图形服从这套规则。2. 美化前必须先想清楚的机制问题PlantUML样式控制链路2.1 skinparam最常用的局部修饰入口PlantUML的样式控制绝大部分都落在skinparam关键字上。你可以把它理解成“主题参数中心”它支持对全局或某一类图形单独设置参数。常见的写法有几种skinparam backgroundColor white skinparam classBackgroundColor #F7F9FC skinparam classBorderColor #B0B8C4也可以把同类参数聚合在一起skinparam class { BackgroundColor #F7F9FC BorderColor #B0B8C4 ArrowColor #2D9CDB FontColor #1E2A3E }第二种写法可维护性更好。当你需要整体调整类图的风格时不用翻找十几行零散的参数只需要在一个代码块里修改。这也是我推荐的基础写法。需要注意的是skinparam几乎可以作用于所有PlantUML支持的图形类型——类class、时序sequence、用例usecase、组件component、部署deployment等。每类图形都有自己的专属参数前缀比如skinparam sequence { ... }、skinparam component { ... }。在写主题时最好分图形类型依次列出这样阅读和维护都会舒服得多。2.2 !include与配置复用让整套主题文件化很多人美化PlantUML是从在每张图里手写几条skinparam开始的这在小场景下没问题但一旦你需要在十张、二十张图里保持风格统一就一定会遇到重复配置的问题。PlantUML提供了!include指令可以在一个PlantUML文件里引入另一个文件。这就像是CSS文件之于HTML把样式抽离出去让图文件的正文只关心内容本身。我实际项目的目录结构一般是这样的docs/ style/ global.puml diagrams/ class-overview.puml sequence-login.pumlglobal.puml只放主题样式不画任何实际的图。每个具体的图表文件开头只需要写上一行!include ../style/global.puml然后继续写你的类、关系、消息。这样一套主题可以覆盖整个文档目录下的所有图表改一处样式全局生效维护成本极低。2.3 主题theme与内建变量的优先级除了skinparamPlantUML还提供了一个半成品的!theme指令。比如!theme cerulean!theme会把plantuml内置的一套主题参数导入。我在实践中发现单纯依赖内置主题只能让图从“难看”变成“不那么难看”很难达到“定制感”的程度。因为内置主题是通用设计不可能考虑到你的文档配色、字体、排版风格。那么!theme和自定义skinparam同时存在时谁生效实际规则是后出现的会覆盖先出现的。如果你先写!theme cerulean再写一堆自定义skinparam你的自定义参数会覆盖主题对应项。反过来如果你先写自定义参数再!theme主题会反过来覆盖你的参数。所以正确的用法是先!include或!theme引入基底然后在后面统一写覆盖参数。这个顺序问题很多人栽过跟头我后面还会专门讲。2.4 优先级规则谁在后谁生效全局与局部的关系总结一下PlantUML样式生效的优先级规则一个具体图形上直接写skinparam例如skinparam class { ... }作用范围是所有类节点。图形内部还可以通过node、class等元素的尖括号语法直接写单个元素的颜色例如class A #red这种方式优先级最高能覆盖skinparam里的同类设置。因此做主题文件时不要频繁对单个元素写颜色只在需要强调少数几个元素时使用“尖括号颜色”其他元素一律交给全局配置。掌握了这几点你才算真正理解了PlantUML样式控制链路后面写主题源码时心里就有谱了。3. 可直接复用的现代化主题源码与逐段讲解3.1 我选择“灰阶一个强调色”的配色设计在动手写主题之前我先定了一个配色方向灰阶打底一个强调色负责所有交互与主链。这个方向参考了现代文档站如GitHub、Stripe等的排版逻辑——大量留白、低饱和背景、用同一种品牌色表达“可点击”或“当前焦点”。具体选色如下用途色值说明背景#FFFFFF纯白避免导出后与文档底色不一致页面底色可选#F8F9FA想走浅灰风格时可全局替换主文字#1E2A3E深蓝灰比纯黑温和次文字#5A6473用于属性、方法名等次要说明边框线#B0B8C4低对比边框不抢内容节点底色#F7F9FC极浅的蓝灰比纯白有质感强调色#2D9CDB一种偏蓝的青用于箭头、高亮边框、参与者的生命线警示色#E5734E用于异常节点、错误提示成功色#27AE60用于状态图的完成态这套配色整体属于“低饱和、冷调偏蓝”的方向。它不会让你第一眼觉得惊艳但放到技术文档、企业wiki、开源源码仓库的README里都会非常协调。重要的不是惊艳而是耐看。3.2 完整主题源码 global.puml下面这份源码我基于PlantUML 1.2023.x以上版本实测过可以直接保存为global.puml然后在你任意一个PlantUML文件顶部include它。所有关键段落我都写了解释。 基础全局设置 skinparam backgroundColor #FFFFFF skinparam shadowing false skinparam roundcorner 8 skinparam ArrowThickness 1.5 skinparam ArrowColor #2D9CDB skinparam defaultTextAlignment center 字体设置 注意defaultFontName 在本地命令行/服务器端可能受系统字体限制。 如果你使用的是线上PlantUML服务最好不指定字体保留默认。 当前配置适合本地有中英文字体的环境示例写的是通用无衬线体。 skinparam defaultFontName Arial, Microsoft YaHei, PingFang SC, Noto Sans CJK SC skinparam defaultFontSize 13 skinparam titleFontSize 18 skinparam titleFontStyle bold skinparam titleFontColor #1E2A3E 类图 skinparam class { BackgroundColor #F7F9FC BorderColor #B0B8C4 ArrowColor #2D9CDB FontColor #1E2A3E AttributeFontColor #5A6473 AttributeFontSize 12 StereotypeFontColor #2D9CDB StereotypeFontSize 10 FontStyle plain } 包/命名空间 skinparam package { BackgroundColor #FFFFFF BorderColor #B0B8C4 FontColor #1E2A3E BorderThickness 1 RoundCorner 10 } 接口 skinparam interface { BackgroundColor #FFFFFF BorderColor #2D9CDB ArrowColor #2D9CDB FontColor #1E2A3E } 枚举 skinparam enum { BackgroundColor #F0F7EE BorderColor #A5C9A1 ArrowColor #2D9CDB FontColor #1E2A3E } 抽象类 skinparam abstract { BackgroundColor #FDF6EC BorderColor #D9B38C ArrowColor #2D9CDB FontColor #1E2A3E } 时序图 skinparam sequence { MessageAlign center ArrowColor #2D9CDB LifeLineBorderColor #2D9CDB LifeLineBackgroundColor #F7F9FC ParticipantBackgroundColor #FFFFFF ParticipantBorderColor #B0B8C4 ParticipantFontColor #1E2A3E NoteBackgroundColor #FFF9E6 NoteBorderColor #E6D3A3 BoxBorderColor #B0B8C4 BoxBackgroundColor #F7F9FC } 组件/部署图 skinparam component { BackgroundColor #FFFFFF BorderColor #B0B8C4 ArrowColor #2D9CDB FontColor #1E2A3E } skinparam node { BackgroundColor #F7F9FC BorderColor #B0B8C4 ArrowColor #2D9CDB FontColor #1E2A3E } skinparam database { BackgroundColor #EAF4FB BorderColor #7FB5D9 ArrowColor #2D9CDB FontColor #1E2A3E } 用例图 skinparam usecase { BackgroundColor #FFFFFF BorderColor #B0B8C4 ArrowColor #2D9CDB FontColor #1E2A3E } skinparam actor { BackgroundColor #FFFFFF BorderColor #B0B8C4 ArrowColor #2D9CDB FontColor #1E2A3E } 状态图 skinparam state { BackgroundColor #FFFFFF BorderColor #B0B8C4 ArrowColor #2D9CDB FontColor #1E2A3E } 注记 skinparam note { BackgroundColor #FFF9E6 BorderColor #E6D3A3 FontColor #1E2A3E } 通用文本与标签 skinparam defaultFontColor #1E2A3E skinparam hyperlinkColor #2D9CDB skinparam hyperlinkUnderline false这份源码的核心思路是先定义全局氛围再分图形类型校准细节。全局部分把圆角、阴影、箭头粗细统一分图形部分保证不同图形之间即使共享色板也不会出现风格漂移。比如类图的边框是淡灰蓝接口的边框却用了强调色这样视觉上能明显区分“普通类”和“对外暴露的接口”。3.3 关键属性逐段解释上面源码里有一些属性是“默默做事”的但非常关键skinparam defaultTextAlignment center让节点里的文字默认居中对齐。PlantUML默认很多节点内部文字是左对齐的在低饱和现代风格下居中对齐会让节点显得更整洁、更“卡片化”。但对属性、方法较多的类来说居中对齐实读体验不一定最好所以这个参数按你的情况取舍。如果你想保持类内部属性左对齐可以改成skinparam defaultTextAlignment left然后单独对title设置居中。skinparam roundcorner 8这是一个全局圆角半径单位是像素。8px是一个比较稳妥的值既不会像0px那样生硬也不会像20px那样卡通。有的人偏好大圆角可以改成12甚至16但建议不要全局统一改成特别大的圆角因为时序图中的生命线、组件图中的若干子组件大圆角反而会让图显得笨重。skinparam ArrowThickness 1.5箭头粗细是影响整张图质感的关键。默认的大黑箭头换成1.5px的蓝色细箭头整张图立刻会从“工程图”变成“设计图”。之所以选1.5而不是1是因为导出PNG时太细的箭头在缩放后可能发虚1.5是一个折中值。skinparam shadowing false去阴影。这是改变“老气感”最有效的一步。一旦阴影消失配合细边框和圆角图就进入了现代扁平化风格。字体设置里我写了多个字体名Arial是英文兜底Microsoft YaHei是Windows下的中文PingFang SC是macOS中文Noto Sans CJK SC是Linux常用中文。这样写一个字体参数理论上在三种系统环境下都能命中一个可用字体。但这个写法有一个隐藏问题如果你用的是在线PlantUML服务服务端操作系统可能没有这些中文字体此时指定字体反而可能导致中文字符显示异常。这就是为什么在配置里我把字体设置单独列出并在注释里反复强调环境差异。3.4 集成方式与两个使用示例把global.puml放到项目文档目录后任意一个PlantUML图文件都可以这样开头startuml !include ../style/global.puml class User { -id: Long -name: String login(): Boolean } class UserService { findUserById(id: Long): User } UserService -- User : 使用 enduml再到命令行执行plantuml -tsvg class-overview.puml或者直接用VS Code的PlantUML插件预览。引入主题文件后这张类图就不再是默认的荧光蓝底黑边框了而是浅灰蓝底、细灰边框、蓝色箭头、文字低饱和整体观感干净很多。再看一个时序图的集成示例startuml !include ../style/global.puml actor 用户 participant Web前端 as web participant 用户服务 as auth 用户 - web : 提交登录请求 web - auth : login(username, password) auth -- web : token web -- 用户 : 登录成功 enduml引入主题后时序图的生命线是清淡的蓝灰色消息箭头是统一的强调蓝消息文字居中排列整个结构比默认纯黑箭头清爽得多。4. 分图型细化类图、时序图、架构图的美化细节4.1 类图如何避免“豆腐块”聚集类图最常见的审美问题是多个类节点紧挨在一起每个节点里塞满了属性和方法看起来像一堆豆腐块。我用PlantUML画类图时除了用global.puml统一基础样式还会特别关注几个点上第一对可见性符号做减法。PlantUML里可以用-、#、表示private、protected、public默认样式中这些符号是直接跟在属性名前面的视觉噪音不小。现代风格中我更习惯把这些可见性符号换成图标模式skinparam classAttributeIconSize 10这个参数可以把-、#、渲染成小图标比默认文本符号精致很多。不过注意这个参数是否生效取决于PlantUML版本我在1.2023.x实测是可以的。第二控制类的显示内容。不是所有类都要把全部属性和方法展示出来。在架构设计文档里类和类之间的关系比内部实现更重要。画图时可以用hidden关键字隐藏不想展示的成员让这个类看起来是一个干净的卡片。图不是越小越丑也不是越满越专业关键是让人一眼抓住重点。第三用包package而不是凌乱坐标。类多了以后不要手动调left、right坐标来控制位置。合理的做法是用package或者namespace把相关类组织在一起再配合together关键字让一组类在布局上保持聚集。PlantUML的自动布局引擎在大多数情况下是靠谱的强行手动挪坐标往往越调越乱。下面是我常用的一张类图配置startuml !include ../style/global.puml skinparam classAttributeIconSize 10 package 领域层 { class User class Order } package 应用层 { class UserService class OrderService } UserService -- User OrderService -- Order enduml这样画出来的类图结构清晰包与包之间有明确的视觉分区类之间是细箭头连接不会出现默认主题那种大黑板一块的感觉。4.2 时序图的阅读节奏优化时序图是PlantUML里使用频率最高的图之一而且它对阅读节奏的要求比类图更高。默认主题下时序图的问题一般是消息箭头纯黑、文字拥挤、生命线太粗整张图看起来像一根根棍子插在地上。我在global.puml里把时序图的MessageAlign设置为center意思是消息文字在箭头上方居中显示这样视觉上比默认左对齐更平衡。另一个值得调的点是参与者的背景色。默认情况下参与者是一个方框里面写名字背景色跟节点差不多的荧光色。我将其改成#FFFFFF白底、#B0B8C4细边框名字用深蓝色文字。这样参与者在图上就像是一张张挂在生命线上的标签不引导画面但存在感恰当。生命周期activate的激活条颜色默认跟生命线一样是黑色的。你可以单独把激活条设置成淡蓝色让它与生命线区分开让读者更清楚地看到一次调用的生命周期范围。比如skinparam sequence { LifeLineBorderColor #2D9CDB LifeLineBackgroundColor #F7F9FC ParticipantBackgroundColor #FFFFFF ParticipantBorderColor #B0B8C4 MessageAlign center }如果想强调某一次调用的时间是长是短可以用和-配合activate与deactivate激活条在视觉上会自动拉长和缩短。配合细色调的激活条时序图会像一个清晰的故事线而不是一堆重叠的方块。4.3 组件/部署/用例图的层级与配色统一组件图、部署图、用例图本质上都是“节点-连线”图它们的美化难点在于节点类型多、层级多很容易被画成大杂烩。我的经验是给不同类型的组件赋予不同的背景色但色相必须来自同一个色板。比如组件component默认用白色容器内部的子组件component用浅蓝灰#F7F9FC数据库节点用更明显的浅青蓝#EAF4FB。这样读者不需要看文字只看节点的颜色深浅就能感知到“这是外层框架那是内部模块那是数据存储”。用例图里的参与者actor与用例usecase之间要有明显的视觉对比。参与者我用白色底加粗边框用例用浅灰蓝底加细边框。这样参与者和用例之间不会互相干扰视觉重点。部署图里的节点node、设备device、数据库database同样遵循同一套配色逻辑。用表格整理我常用的对应关系会更直观图形类型背景色边框色用途component#FFFFFF#B0B8C4普通组件/服务component 子模块#F7F9FC#D0D7E0被嵌套的内部模块database#EAF4FB#7FB5D9数据库、缓存中间件node#F0F7EE#A5C9A1部署节点/主机interface#FFFFFF#2D9CDB对外暴露的接口queue / 消息队列#FDF3E7#D9B38C消息队列、异步通道这套分工的好处是读者扫一眼整张架构图就能凭颜色判断出图中哪些是存储、哪些是计算、哪些是通信渠道。架构图的信息传达效率会因此高很多。4.4 针对这些图我的差异化调优参数在全局主题的基础上针对不同的图我会额外追加一小段参数。比如组件图里我希望组件之间的依赖关系更清晰可以在文件顶部单独写skinparam componentStyle rectangle skinparam ArrowThickness 1.2 skinparam component { BackgroundColor #FFFFFF BorderColor #B0B8C4 FontColor #1E2A3E }componentStyle rectangle是一个值得单独提的参数。默认情况下组件是横向洋梨形类似电路图中电阻符号在架构图里这个形状占面积大也不够现代。改成rectangle后组件变成矩形整个布局会紧凑很多观感也更符合当前主流架构图风格。用例图里我会额外写skinparam usecase { BackgroundColor #F7F9FC BorderColor #B0B8C4 FontColor #1E2A3E } skinparam actorStyle awesomeactorStyle awesome会把默认的火柴人观众席风格换成一个简洁的人形图标确实会比火柴人好看一截。这个参数在较新版本中是支持的老版本可能不支持但多数主流在线PlantUML服务已经更新到新版本可以放心用。5. 美化实战中绕不开的坑和排查方法5.1 中文字体问题为什么导出后全是方框这是整个美化过程中最容易踩的坑尤其是你在本地命令行渲染带中文的PlantUML图时。默认情况下PlantUML依赖Java的字体渲染能力如果你用的服务器或本机没有合适的中文字体导出的PNG、SVG里中文会变成一个个方块。排查思路是这样的第一步确认执行PlantUML的机器上是否安装了中文字体。以Linux服务器为例可以用fc-list | grep -i cjk查看是否有中文字体macOS一般自带PingFang SCWindows一般自带微软雅黑。如果你在服务器上跑PlantUML而且从来没装过中文字体那基本都会出问题。第二步不要一股脑把多个字体名塞进defaultFontName里。虽然我在前面的主题源码里写了多个字体名但这是为了兼顾不同平台。如果一个平台没有按顺序匹配到第一个字体理论上会继续找下一个但实际Java字体匹配逻辑并不总是如你所愿。最稳妥的办法是确认当前运行环境的系统字体然后明确指定一个已知存在的中文字体名。第三步如果只是偶尔用在线PlantUML服务渲染图片建议不在defaultFontName里指定中文字体而是让服务端用它的默认字体。很多在线服务端已经内置了中文字体缺省时反而正常一旦你指定了一个它没有的字体它就会放弃原有中文字体导致方块。5.2 高分辨率导出模糊或截断DPI与大小控制PlantUML默认渲染PNG时分辨率一般不高。当你把图片放大到文档或PPT里时会明显模糊。我的做法是优先导出SVG格式而不是PNG。plantuml -tsvg architecture.pumlSVG是矢量图放到任何地方都不会糊。如果你的写作工具或文档系统不支持SVG导入那就退而求其次在导出PNG时用高分辨率参数plantuml -tpng -resolution 200 architecture.puml在较新的PlantUML版本中还可以用skinparam dpi 200直接控制导出的缩放比例。但要注意把dpi调大以后图的整体尺寸也会放大可能超出页面宽度。遇到这种情况可以配合skinparam maxMessageSize或调整-width、-height参数来控制最终输出尺寸。另一个常见问题是导出PNG后图的右侧或底部被截断。这通常是因为图里某些元素被手动设置了坐标或者注记note的位置脱离了图的主体布局范围。排查时先去掉所有手动的坐标参数让PlantUML自动布局看是否还截断如果还截断再检查是不是某个节点被设定成了肉眼不可见但占据位置的状态。5.3 include顺序与覆盖失效使用!include统一主题后如果图文件里自己又写了相同属性的skinparam到底谁生效我在前面提过规则后写的覆盖先写的。实际中更隐蔽的问题是如果你有两个include文件比如base.puml和theme.puml而它们里面对同一个属性有冲突那么以最后一个include进来的文件为准。所以在组织主题文件目录时我习惯遵循“底层基础放前面定制覆盖放后面”的顺序。维护一个团队级主题时最好只有一个主题文件被各个图文件引用避免多个样式文件互相覆盖导致排查困难。如果你发现无论如何设置某些元素的外观都不变还有一个可能该元素类型没有被你的skinparam覆盖到。比如skinparam class { ... }只管类图对接口类型不生效对接口要用skinparam interface { ... }。很多人在一个参数上折腾半天最后发现作用错了对象。5.4 布局重叠时优先改这些参数图元素一多自动布局偶尔会重叠或挤在一起。这时候不要慌着手动给每个节点加坐标应该按优先级做以下调整调整skinparam nodesep和skinparam ranksep。前者控制同一层节点之间的水平间距后者控制不同层节点之间的垂直间距。间距调大图自然就舒展了。调整skinparam padding。这个参数控制节点内部文字与边框的留白。Padding太小文字挤边Padding太大节点臃肿。调整skinparam minclasswidth。类图尤其会遇到类节点太扁的情况设置一个最小宽度可以让类名和属性排列更好看。使用together关键字强制相关节点靠近避免自动布局把它们拆散。我画组件图时遇到布局混乱通常先ranksep和nodesep各加10到20图马上就会透气很多。如果你需要非常精确的布局那PlantUML可能不是最合适的工具考虑Graphviz或draw.io。5.5 把整套风格沉淀为团队基础文件最后分享一个我在团队协作中的做法不要只在个人文件里放一套好看的全局主题而是把global.puml提交到代码仓库的docs/style/目录里然后在团队文档规范里规定新画的所有PlantUML图第一行必须include这个文件。这样做的直接收益是十几个人的团队画出来的图风格统一代码评审和架构文档都干净很多。更深层的收益是后来者不需要从头琢磨怎么调好看不需要在每张图里复制几十行样式代码只需要关注内容本身。我维护这套主题文件的过程中最大的体会是审美不是靠堆参数实现的而是要先把颜色、线条、留白这些基础规则定清楚然后再让所有图都去遵守这些规则。PlantUML真正给了你自定义的能力但最终的图好不好看取决于你有没有一套“受控”的视觉体系而不是会不会写更多的skinparam。
分享:

看完干货,该让你的企业上线了

免费需求沟通 · 48 小时内出具建站方案 · 河南本地可上门