资讯动态

纯HTML+SVG图解工具:出版级架构图的语义化生成方案

发布时间:2026/9/15 23:59:43 来源:尧图企业网站定制
1. 项目概述为什么一个纯前端图解工具能登上GitHub热榜“GitHub每日热评diagram-design源码深度评测告别粗糙架构图纯HTMLSVG实现设计师也认可的出版级图解”——这个标题里藏着三个关键信号它不是又一个流程图编辑器不是基于Canvas或WebGL的重型渲染引擎更不是依赖后端服务的SaaS工具。它是一套完全运行在浏览器里的、用原生HTMLSVG构建的轻量级图解生成系统核心目标直指工程师与设计师之间长期存在的协作断层工程师画的架构图线条歪斜、字体混杂、对齐随意设计师接手后第一反应是“重做”而不是“微调”。我第一次看到diagram-design仓库时正被团队一张K8s集群拓扑图折磨得焦头烂额。开发同学用draw.io导出的PNG放大后边缘发虚文字字号不统一服务模块之间的连接线粗细不一致UI同事拿到后直接说“这没法进设计规范文档得重画。”而diagram-design的README第一行就写着“No dependencies. No build step. No server required.”——没有依赖、无需构建、不靠服务器。它只认一个东西你写在HTML里的结构化语义标签。它的核心价值不在“能画什么”而在“怎么让画出来的东西天然符合出版级标准”。比如它把diagram作为根容器内部用node定义节点edge定义连线label标注文字——这些不是自定义元素而是通过customElements.define()注册的真实Web Component。这意味着你写的不是一堆divcss模拟的图形而是浏览器原生理解的语义化图元。SVG渲染层自动接管所有坐标计算、路径生成、文本排版和响应式缩放连text元素的dominant-baseline和text-anchor都按印刷排版规范预设好了。我实测过在1920×1080屏上导出的SVG直接拖进InDesign里字号、行高、字间距全部对齐Adobe默认值连设计师都惊讶地问“你们是不是偷偷接了Adobe API”关键词HTML和SVG在这里不是技术栈罗列而是设计哲学用语义化HTML组织逻辑关系用声明式SVG保证视觉精度。它拒绝把“画图”变成“写JS控制DOM”而是让你像写文章一样写图解——先搭骨架HTML结构再定样式CSS变量最后由SVG引擎自动完成像素级渲染。这种思路直接绕开了传统图表库常见的“状态同步失真”问题你改一个节点位置连线自动重算锚点你换一种主题色所有边框、文字、阴影同步更新没有一处需要手动getElementById().setAttribute()。适合谁如果你是后端工程师需要给技术方案文档配图但不想学Figma快捷键如果你是前端架构师要输出系统演进路线图却苦于Visio导出PDF糊成一片如果你是技术文档工程师每天被产品经理追问“这张图能不能加个箭头说明数据流向”——那么diagram-design不是“又一个工具”而是你文档工作流里缺失的那块拼图。它不追求功能大全但每项能力都卡在出版级交付的临界点上支持CMYK色彩模式预览、内置LaTeX数学公式渲染、导出带嵌入字体的SVG非webfont fallback、自动为连线添加正交/贝塞尔路径平滑算法。这些细节才是它冲上GitHub热榜的真实原因——不是因为“能用”而是因为“用完不用返工”。2. 核心设计逻辑拆解为什么放弃Canvas和React死磕原生HTMLSVGdiagram-design最反直觉的选择是彻底放弃当前主流图表库的技术路径不基于Canvas做像素绘制不依赖React/Vue做虚拟DOM diff甚至不引入任何第三方渲染引擎如D3.js或Snap.svg。它的源码目录结构干净得令人不安——只有src/下四个文件diagram.js主组件、node.js节点类、edge.js连线类、renderer.jsSVG渲染器。没有node_modules没有webpack.config.js连package.json都只有一行type: module。这种极简主义背后是一整套针对“出版级图解”场景的精准取舍逻辑。2.1 放弃Canvas的根本原因精度不可控Canvas的本质是位图绘制所有图形最终都转化为像素点阵。当你在Canvas里画一条1px宽的线在2x Retina屏上实际占2物理像素但浏览器渲染时可能因抗锯齿算法导致边缘半透明。diagram-design作者在issue里明确写道“出版级图解的第一要求是‘可无限缩放不失真’Canvas天生违背这一原则。”我做过对比测试用相同坐标画一个矩形节点Canvas导出PNG在InDesign里放大400%后出现明显阶梯状锯齿而SVG导出后无论放大多少倍边缘始终锐利如刀切。更关键的是文本渲染——Canvas里fillText()无法精确控制字距kerning和基线baseline中文标点常与英文字符错位而SVG的text元素原生支持letter-spacing、word-spacing和alignment-baseline连全角/半角空格的宽度差异都能按Unicode标准精确处理。提示Canvas适合游戏、实时数据可视化等对帧率敏感的场景但技术文档图解的核心诉求是“静态精度”而非“动态流畅”。选型错误会导致后续所有优化都在对抗底层缺陷。2.2 拒绝React框架的深层考量状态与视图强耦合风险React的单向数据流在复杂交互场景中优势明显但图解生成恰恰是“低交互、高确定性”的任务。diagram-design的典型使用场景是工程师写好HTML结构 → 浏览器解析 → SVG自动渲染 → 导出静态文件。整个过程无用户实时拖拽、无动态增删节点、无动画过渡。如果强行套用React会引入三重冗余虚拟DOM开销每次修改节点属性都要触发reconcile而图解结构变更频次极低通常一次文档只改1-2处这部分CPU消耗纯属浪费Props传递链路Diagram→Node→Label→Edge的props层层透传使代码可读性下降调试时需追踪多层组件状态SSR兼容性陷阱React服务端渲染的SVG输出常因useEffect时机问题导致坐标计算错误而原生Web Component在Node.js环境可通过jsdom完美复现浏览器行为。作者在源码注释里留下一句很实在的话“If your diagram doesn’t need to respond to mouse events in real time, don’t pay for the framework tax.”如果你的图解不需要实时响应鼠标事件就别为框架付费。我实测过一个含50个节点的拓扑图原生方案首次渲染耗时12ms同等结构的React版本耗时47ms——多出的35ms全花在虚拟DOM diff和props绑定上而这对静态图解毫无价值。2.3 坚持HTMLSVG的底层逻辑语义即结构结构即样式diagram-design的革命性在于它把HTML从“内容容器”升维为“图解语法”。看这段真实代码diagram themedark node idapi labelAPI Gateway shaperounded-rect width160 height60 label font-size14API网关/label /node node idauth labelAuth Service shapecircle radius40 label font-size12认证服务/label /node edge fromapi toauth typesolid stroke-width2 label positionmidJWT验证/label /edge /diagram这里node不是div而是继承自HTMLElement的自定义类其connectedCallback()方法会自动触发renderer.js中的坐标计算edge的from/to属性不是字符串而是实时绑定的DOM引用当node idapi位置变化时连线端点自动重算。这种“HTML即DSL领域特定语言”的设计让图解维护成本断崖式下降——修改一个服务名称只需改label里的文字无需同步更新JS里的data数组调整节点尺寸直接改width/height属性渲染器会重新布局所有关联元素。更精妙的是主题系统。themedark不是简单的CSS class切换而是通过CSS Custom Properties注入整套设计系统:root { --diagram-node-fill: #ffffff; --diagram-node-stroke: #333333; --diagram-edge-stroke: #666666; } [themedark] { --diagram-node-fill: #1a1a1a; --diagram-node-stroke: #cccccc; --diagram-edge-stroke: #999999; }SVG渲染器直接读取这些CSS变量生成rect fillvar(--diagram-node-fill) strokevar(--diagram-node-stroke)确保设计规范100%落地。这种“HTML结构 CSS变量 SVG声明式渲染”的铁三角才是它获得设计师认可的真正底牌——设计师只需维护一套CSS变量就能全局控制所有图解的视觉风格无需打开Figma文件逐个修改。3. 源码核心机制深度解析从HTML标签到出版级SVG的完整链路要真正理解diagram-design为何能产出“设计师也认可”的图解必须拆解它从解析HTML到生成SVG的完整链路。这套机制不依赖任何外部库全部由不到800行核心代码驱动分为四个原子环节HTML语义解析 → 节点拓扑建模 → 坐标空间计算 → SVG声明式渲染。每个环节都针对出版级需求做了极致优化下面逐层展开。3.1 HTML语义解析如何让浏览器原生理解“图解语法”diagram-design的起点是customElements.define(diagram, DiagramElement)它注册了一个名为diagram的自定义元素。关键在于这个类的connectedCallback()方法并非简单地appendChild()而是启动了一套深度遍历算法class DiagramElement extends HTMLElement { connectedCallback() { // 1. 构建节点索引表{id: nodeElement} this._nodes new Map(); // 2. 构建边索引表[{fromId, toId, element}] this._edges []; // 3. 深度优先遍历子元素识别语义标签 const walk (el) { if (el.tagName NODE) { const id el.getAttribute(id); if (id) this._nodes.set(id, el); } else if (el.tagName EDGE) { const from el.getAttribute(from); const to el.getAttribute(to); if (from to) this._edges.push({ from, to, element: el }); } el.children.forEach(walk); }; walk(this); // 4. 触发渲染 this._render(); } }这段代码的精妙之处在于利用浏览器原生DOM树遍历能力替代JSON Schema校验。传统图表库需要先定义schema如{ nodes: [...], edges: [...] }再用JS解析JSON生成DOM而diagram-design直接让开发者用HTML写“图解”浏览器解析HTML时已自动构建好DOM树它只需从中提取语义信息。这带来两大优势零学习成本工程师无需学新语法写HTML的经验直接复用强类型保障HTML parser会自动过滤非法标签如node id会被视为无效getAttribute(id)返回null避免运行时类型错误。我曾故意在node里漏写id属性结果_edges数组里对应from/to的边直接被跳过——不是报错而是静默忽略。这种“fail-fast but graceful”的设计比抛出TypeError: Cannot read property x of undefined友好得多。3.2 节点拓扑建模用图论算法解决布局冲突diagram-design默认采用力导向布局Force-Directed Layout但实现方式与D3.js截然不同。它不模拟物理粒子而是将布局问题转化为图论中的“层次化布局”Hierarchical Layout问题。核心算法在layout.js中仅62行// 输入节点ID映射表 边关系数组 function calculateLayout(nodes, edges) { // 步骤1构建邻接表Adjacency List const graph new Map(); nodes.forEach(node graph.set(node.id, new Set())); edges.forEach(edge { graph.get(edge.from)?.add(edge.to); }); // 步骤2计算节点层级Layer Assignment // 使用Kahn算法进行拓扑排序处理有向无环图DAG const inDegree new Map(); nodes.forEach(n inDegree.set(n.id, 0)); edges.forEach(e inDegree.set(e.to, (inDegree.get(e.to) || 0) 1)); const queue []; nodes.forEach(n { if (inDegree.get(n.id) 0) queue.push(n.id); }); const layers new Map(); // { nodeId: layerIndex } let currentLayer 0; while (queue.length 0) { const size queue.length; for (let i 0; i size; i) { const nodeId queue.shift(); layers.set(nodeId, currentLayer); // 更新下游节点入度 graph.get(nodeId)?.forEach(neighbor { const newIn inDegree.get(neighbor) - 1; inDegree.set(neighbor, newIn); if (newIn 0) queue.push(neighbor); }); } currentLayer; } // 步骤3同层节点水平居中排列X坐标 const layerNodes new Map(); layers.forEach((layer, nodeId) { if (!layerNodes.has(layer)) layerNodes.set(layer, []); layerNodes.get(layer).push(nodeId); }); // 步骤4为每层分配Y坐标节点在层内按顺序X坐标 const positions new Map(); layerNodes.forEach((nodeIds, layer) { const y layer * 120 60; // 层间距120px首层偏移60px nodeIds.forEach((nodeId, index) { const x 200 index * 240; // 同层节点水平间距240px positions.set(nodeId, { x, y }); }); }); return positions; }这个算法的关键创新是用拓扑排序替代物理模拟。对于微服务架构图这类天然存在依赖关系A→B→C的场景Kahn算法能精准识别执行顺序确保上游服务总在下游服务上方。我测试过一个含12个服务的电商系统图传统力导向布局常出现支付服务在订单服务下方的逻辑倒置而diagram-design的拓扑排序布局100%保证了“用户请求→API网关→订单服务→支付服务→消息队列”的垂直流向。注意当图中存在环如A→B→A时Kahn算法会检测到inDegree无法归零自动降级为网格布局Grid Layout避免无限循环。这种兜底机制在真实架构图中极为实用——毕竟不是所有系统都能严格分层。3.3 坐标空间计算像素级精度的数学基础出版级图解的核心痛点是“所见即所得”。diagram-design的坐标系统设计直击要害所有尺寸单位强制为px像素禁用em/%/rem等相对单位。源码中所有getBoundingClientRect()调用都包裹在window.devicePixelRatio校准逻辑里// 在renderer.js中 function getPixelPerfectRect(element) { const rect element.getBoundingClientRect(); // 针对Retina屏校准物理像素 CSS像素 × devicePixelRatio const scale window.devicePixelRatio || 1; return { x: rect.left * scale, y: rect.top * scale, width: rect.width * scale, height: rect.height * scale }; }这意味着当你设置node width160 height60渲染器生成的SVGrect元素宽高就是160×60物理像素无论屏幕DPR是1、2还是3。对比传统方案CSS中width: 160px在2x屏上实际占320物理像素但若未启用image-rendering: -webkit-optimize-contrast浏览器可能模糊渲染而diagram-design直接在SVG层面操作物理像素彻底规避此问题。更硬核的是文本排版精度控制。SVG的text元素默认基线对齐方式dominant-baseline为alphabetic但中文排版需要central。源码中强制重写// 为所有text元素设置出版级基线 const textElement document.createElementNS(http://www.w3.org/2000/svg, text); textElement.setAttribute(dominant-baseline, central); textElement.setAttribute(text-anchor, middle); // 字体渲染强制开启subpixel antialiasing textElement.style.fontSmoothing antialiased; textElement.style.webkitFontSmoothing antialiased;我用同一段中文在Chrome/Firefox/Safari中测试dominant-baselinecentral使标题文字垂直居中误差小于0.3px而默认alphabetic在Firefox中偏差达2.7px——这对需要精确对齐的出版物是致命缺陷。3.4 SVG声明式渲染如何让XML代码具备设计系统灵魂最终生成的SVG不是简单拼接字符串而是通过document.createElementNS()创建真实DOM节点再注入CSS变量。关键代码在renderer.jsfunction renderToSVG(diagramElement, positions) { const svg document.createElementNS(http://www.w3.org/2000/svg, svg); svg.setAttribute(viewBox, 0 0 ${diagramElement.clientWidth} ${diagramElement.clientHeight}); svg.setAttribute(width, 100%); svg.setAttribute(height, 100%); // 渲染节点 diagramElement._nodes.forEach((nodeEl, nodeId) { const pos positions.get(nodeId); const rect document.createElementNS(http://www.w3.org/2000/svg, rect); rect.setAttribute(x, pos.x - 80); // 宽度160居中需减半 rect.setAttribute(y, pos.y - 30); // 高度60居中需减半 rect.setAttribute(width, 160); rect.setAttribute(height, 60); rect.setAttribute(rx, 8); // 圆角 rect.setAttribute(fill, var(--diagram-node-fill)); rect.setAttribute(stroke, var(--diagram-node-stroke)); rect.setAttribute(stroke-width, 1.5); svg.appendChild(rect); // 渲染标签 const label nodeEl.querySelector(label); if (label) { const text document.createElementNS(http://www.w3.org/2000/svg, text); text.textContent label.textContent; text.setAttribute(x, pos.x); text.setAttribute(y, pos.y); text.setAttribute(font-size, label.getAttribute(font-size) || 14); text.setAttribute(fill, var(--diagram-text-color)); text.setAttribute(dominant-baseline, central); text.setAttribute(text-anchor, middle); svg.appendChild(text); } }); // 渲染连线贝塞尔曲线 diagramElement._edges.forEach(edge { const fromPos positions.get(edge.from); const toPos positions.get(edge.to); const path document.createElementNS(http://www.w3.org/2000/svg, path); // 生成三次贝塞尔曲线控制点取中点偏移 const cx1 fromPos.x (toPos.x - fromPos.x) * 0.3; const cy1 fromPos.y; const cx2 toPos.x - (toPos.x - fromPos.x) * 0.3; const cy2 toPos.y; path.setAttribute(d, M${fromPos.x},${fromPos.y} C${cx1},${cy1} ${cx2},${cy2} ${toPos.x},${toPos.y}); path.setAttribute(stroke, var(--diagram-edge-stroke)); path.setAttribute(stroke-width, edge.element.getAttribute(stroke-width) || 2); path.setAttribute(fill, none); svg.appendChild(path); }); return svg; }这段代码揭示了它“设计师认可”的终极秘密SVG元素直接消费CSS Custom Properties。设计师修改--diagram-node-fill变量所有rect的fill属性自动更新无需重新运行JS。我让UI同事试用时她只改了3个CSS变量就完成了整套深色主题适配全程没碰一行JS——这才是真正的设计开发协同。4. 实操全流程详解从零开始生成一张出版级架构图现在我们动手实操用diagram-design生成一张真实的微服务架构图。整个过程无需安装Node.js、不需npm run dev、不依赖任何构建工具——只要一个文本编辑器和现代浏览器。我会以“用户中心服务架构”为例展示从空白HTML到可交付SVG的完整链路并标注每个步骤的决策依据。4.1 环境准备三分钟搭建零依赖工作区第一步创建一个纯静态HTML文件user-center-diagram.html。不要用VS Code新建项目直接右键→新建文本文档→重命名为.html。内容从最简HTML骨架开始!doctype html html langzh-cn head meta charsetutf-8 meta nameviewport contentwidthdevice-width, initial-scale1.0 title用户中心架构图/title !-- 关键直接引入diagram-design的ESM模块 -- script typemodule import { Diagram } from https://cdn.jsdelivr.net/npm/diagram-design1.2.0/dist/diagram.min.js; // 注册自定义元素 customElements.define(diagram, Diagram); /script style /* 出版级打印样式隐藏滚动条固定尺寸 */ body { margin: 0; padding: 40px; background: #ffffff; } diagram { width: 1200px; height: 800px; display: block; } media print { body { padding: 0; } diagram { width: 100%; height: auto; } } /style /head body !-- 图解容器将在此处插入 -- /body /html这里有几个必须注意的细节CDN选择使用jsdelivr.net而非unpkg.com因为前者对国内访问更稳定且支持version精确锁定避免latest导致的breaking changescript typemodule这是现代浏览器原生ESM支持无需Babel转译import语句直接生效style中的media print为后续导出PDF做准备确保打印时图表铺满纸张不留白边。实操心得我最初尝试用script src...引入结果报错Uncaught SyntaxError: Cannot use import statement outside a module。后来才意识到diagram-design发布的是ESM格式必须用typemodule。这个坑踩过三次建议新手直接复制上面的模板。4.2 编写语义化图解用HTML定义架构逻辑在body内插入diagram标签开始编写架构图。记住核心原则HTML定义关系CSS定义样式JS不参与业务逻辑。body diagram themelight layouthierarchical !-- 用户服务节点 -- node iduser-api labelUser API shaperounded-rect width160 height60 label font-size14用户API/label label font-size12 positionbottomRESTful接口/label /node !-- 认证服务节点 -- node idauth-service labelAuth Service shapecylinder width120 height80 label font-size14认证服务/label label font-size12 positionbottomJWT/OAuth2/label /node !-- 用户数据库节点 -- node iduser-db labelUser DB shapedatabase width140 height100 label font-size14用户数据库/label label font-size12 positionbottomMySQL 8.0/label /node !-- 缓存服务节点 -- node idcache labelCache shapecloud width130 height70 label font-size14缓存服务/label label font-size12 positionbottomRedis Cluster/label /node !-- 连线定义 -- edge fromuser-api toauth-service typesolid stroke-width2 label positionmidToken校验/label /edge edge fromuser-api touser-db typedashed stroke-width1.5 label positionmid用户查询/label /edge edge fromuser-api tocache typedotted stroke-width1 label positionmid缓存读取/label /edge edge fromauth-service touser-db typesolid stroke-width2 label positionmid密码验证/label /edge /diagram /body关键参数解析themelight激活浅色主题对应CSS变量--diagram-node-fill: #fffffflayouthierarchical显式指定拓扑排序布局避免自动检测失败shape属性rounded-rect圆角矩形、cylinder圆柱体、database数据库图标、cloud云朵——这些不是图片而是SVG路径预设positionbottom标签定位支持top/bottom/left/right/mid五种方位type属性solid/dashed/dotted对应CSS的stroke-dasharray值。注意事项所有id值必须唯一且edge fromxxx中的xxx必须与某个node idxxx完全匹配大小写敏感。我曾因user-api写成User-API导致连线消失调试时用浏览器开发者工具检查_edges数组才发现问题。4.3 主题定制与设计规范落地设计师提供了一套品牌规范主色#2563eb蓝色、强调色#ef4444红色、字体HarmonyOS Sans, Segoe UI, sans-serif。我们通过CSS Custom Properties注入style :root { --diagram-primary: #2563eb; --diagram-accent: #ef4444; --diagram-font-family: HarmonyOS Sans, Segoe UI, sans-serif; } [themelight] { --diagram-node-fill: #ffffff; --diagram-node-stroke: var(--diagram-primary); --diagram-edge-stroke: #6b7280; --diagram-text-color: #1f2937; } [themelight] [shapedatabase] { --diagram-node-fill: #f9fafb; --diagram-node-stroke: #374151; } [themelight] [typedashed] { --diagram-edge-stroke: var(--diagram-accent); } /style这里体现diagram-design的高级特性CSS选择器可穿透到自定义元素内部。[themelight] [shapedatabase]能精准命中数据库节点为其设置专属填充色。我让设计师确认后她只改了3行CSS就完成了品牌色适配而传统方案需要修改JS里的颜色配置数组。4.4 导出出版级SVG与PDF生成图解后右键→“另存为”只能保存HTML页面无法获取纯净SVG。正确导出方式是打开浏览器开发者工具F12→ Elements面板展开diagram元素找到其内部生成的svg节点右键该svg→ “Copy” → “Copy outerHTML”新建文本文件粘贴内容保存为user-center.svg。导出的SVG代码包含完整CSS变量引用可在Illustrator/InDesign中直接打开。若需PDF用Chrome打印功能CtrlP → 目标打印机选“另存为PDF”页面设置尺寸选“A4”方向“横向”边距“最小”更重要的是勾选“背景图形”否则CSS变量颜色不显示点击“保存”。我实测导出的PDF在Acrobat中放大至800%文字边缘依然锐利所有连线粗细一致2px实线、1.5px虚线、1px点线完全符合出版社印刷要求。实操技巧为批量导出我写了一个小脚本放在HTML底部script function exportSVG() { const svg document.querySelector(diagram svg); const serializer new XMLSerializer(); const svgString serializer.serializeToString(svg); const blob new Blob([svgString], {type: image/svgxml}); const url URL.createObjectURL(blob); const a document.createElement(a); a.href url; a.download user-center.svg; a.click(); } /script button onclickexportSVG()导出SVG/button点击按钮即可一键下载比手动复制高效十倍。5. 常见问题与避坑指南那些官方文档不会告诉你的实战经验尽管diagram-design文档简洁优雅但在真实项目落地时仍会遇到一些“看似简单却卡住半天”的问题。以下是我在三个大型项目中踩过的坑以及对应的解决方案。这些问题都不在GitHub Issues里因为它们属于“使用场景错配”而非代码缺陷。5.1 问题速查表高频故障与定位路径现象可能原因快速定位方法解决方案连线不显示from/toID拼写错误或节点未定义id属性在Console执行document.querySelector(diagram)._nodes.size应等于节点数._edges.length应等于连线数检查所有node idxxx和edge fromxxx的ID是否完全一致区分大小写节点重叠堆叠图中存在环A→B→A拓扑排序失败降级为网格布局查看Console是否有Cycle detected, fallback to grid layout警告手动添加layoutgrid属性或重构架构图消除环依赖中文文字模糊未启用亚像素抗锯齿在开发者工具Elements面板检查text元素是否有style-webkit-font-smoothing: antialiased;在style中添加text { -webkit-font-smoothing: antialiased; }全局样式导出SVG无颜色CSS变量未被SVG继承查看导出的SVG源码搜索var(--diagram-node-fill)若存在则正常若被替换为#ffffff则说明变量已计算确保导出的是svg的outerHTML而非diagram的innerHTML后者不含CSS变量响应式失效diagram元素未设置固定宽高检查diagram的computed stylewidth/height是否为auto在style中强制设置diagram { width: 1200px; height: 800px; }5.2 真实避坑案例从“无法交付”到“设计师点赞”案例背景某金融客户要求在技术白皮书中嵌入“风控决策引擎架构图”需满足印刷要求300dpiCMYK色彩。初始版本用draw.io导出PDF放大后文字发虚且红色#ef4444在CMYK模式下偏橙。排查过程第一步用diagram-design重写HTML导出SVG第二步在Illustrator中打开SVG发现所有颜色仍是RGB模式第三步查阅源码发现renderer.js中fill属性直接写var(--diagram-node-fill)而Illustrator不识别CSS变量第四步在导出前用脚本将CSS变量替换为实际值function resolveCSSVariables() { const svg document.querySelector(diagram svg); const style getComputedStyle(document.documentElement); const vars [--diagram-node-fill, --diagram-node-stroke, --diagram-edge-stroke]; vars.forEach(varName { const value style

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价