跳转至

SVG 排错清单

症状查表。找到你的现象,照着「排查动作」一步步做。


通用三板斧(任何问题先做这三件)

1. 先验证 XML 是否合法

SVG 本质是 XML,语法要求比 HTML 严格得多——标签没闭合、属性值没加引号,整张图直接白屏,且没有任何报错提示

一行命令验(需要 Python):

python -c "import xml.dom.minidom; xml.dom.minidom.parse('你的文件.svg'); print('XML OK')"
  • 输出 XML OK → 语法没问题,继续查别的原因。
  • 报错并给出行号 → 直接定位到那一行修。

2. 用浏览器直接打开 .svg 文件

把文件拖进 Chrome / Edge(或用 VS Code 的 SVG 预览插件)。这一步把"站点/构建"这一层因素排除掉

  • 浏览器直接打开就是坏的 → 问题在 SVG 文件本身。
  • 浏览器打开是好的、放进站点后坏了 → 问题在引用方式或路径,见 亮暗双主题与 MkDocs 嵌入

3. 二分法定位(对付"几百行改了一处就坏了")

  1. 备份一份文件。
  2. 注释掉文件后半部分(用 <!-- ... --> 包住),看前半部分是否正常。
  3. 正常 → 问题在后半段;不正常 → 问题在前半段。
  4. 对有问题的那半边重复此操作,几轮就能缩到十几行以内。

注释不能嵌套

<!-- <!-- 内层 --> --> 是非法的。如果原文件里已有注释,二分时不要用包裹式注释,改成直接删除目标段落(记得先备份)。


症状对照表

图完全不显示 / 白屏 / 只显示一个破图标

可能原因 怎么确认 解法
XML 语法错误 三板斧第 1 步报错 按报错行号修。最常见:标签未闭合、属性值缺引号、& 未转义
文件编码问题 文件含 BOM 或被存成了 UTF-16 用编辑器另存为 UTF-8(无 BOM)
路径 / 404 浏览器开发者工具 Network 面板看该图请求状态 亮暗双主题与 MkDocs 嵌入 §路径与文件名编码
<svg> 缺命名空间 检查根标签有无 xmlns="http://www.w3.org/2000/svg" 补上。内联在 HTML5 里可省略,独立 .svg 文件必须有

特殊字符转义是最隐蔽的一种:

<!-- ❌ 非法:裸 & 会让解析器报错 -->
<text>电压 & 电流</text>

<!-- ✅ 正确 -->
<text>电压 &amp; 电流</text>

需要转义的字符:&&amp;<&lt;>&gt;


图只显示了一部分 / 边缘被裁掉

可能原因 怎么确认 解法
内容超出 viewBox 找出所有元素的最大 x/y,与 viewBox 的宽高对比 加大 viewBox 后两个数字(viewBox="0 0 920 720"0 0 920 800
clipPath 裁剪 搜文件里有没有 clip-path 调整裁剪区域,或去掉 clip-path 属性
外层容器 CSS 溢出隐藏 浏览器里检查父元素的 overflow 给容器加 overflow: visible,或缩小 SVG

快速判断是否超出 viewBox

临时在文件末尾(最后一个子元素之后、</svg> 之前)加一个覆盖全画布的红框:

<rect x="0" y="0" width="920" height="720"
      fill="none" stroke="red" stroke-width="2"/>
如果某些元素画到了红框外面,说明它们超出了 viewBox 的可见范围。看完记得删掉这个框。


改了但没效果

可能原因 怎么确认 解法
改错元素 读图第 ④ 步 的亮色试改验证 重新定位
被后面的元素覆盖 SVG 后绘制的在上层——后面的元素会盖住前面的 把目标元素往后移(移到同层最后),或给它加 z-index(仅内联时有效)
浏览器缓存了旧图 强制刷新(Ctrl+F5);或给 URL 加个无关参数 demo.svg?v=2
元素被隐藏 检查有没有 display="none" / visibility="hidden" / opacity="0" 去掉隐藏属性
改的是 <defs> 里的定义,但引用处覆盖了样式 检查 <use> 元素上是否单独写了 fill/stroke 引用处的样式优先级更高,改引用处

SVG 没有 z-index,靠绘制顺序决定层叠

内联 SVG 时才可以用 CSS 的 z-index。作为独立 .svg 文件时,唯一的层叠规则就是:写在后面的元素盖在上面。


元素跑到了意料之外的位置

可能原因 怎么确认 解法
嵌套 <g transform> 累加 从目标元素往上一级级找父 <g>,把所有 transform 加起来 改图的六种常见手术 手术四
旋转中心不对 检查 rotate(角度, cx, cy) 里的 cx, cy 省略中心时会绕原点 (0,0) 转,通常不是你想要的。补上旋转中心
<use> 的 x/y 是平移 <use href="#n" x="50" y="30"> 等价于整体平移 (50,30) 定义时把图元画在原点附近,引用时定位最直观
y 轴方向搞反 想上移却加大了 y y 轴朝下:上移 = y 减小

旋转是新手最容易翻车的一项

<!-- ❌ 绕原点旋转,元素会"飞走" -->
<rect x="100" y="100" width="40" height="20" transform="rotate(45)"/>

<!-- ✅ 绕自身中心旋转 -->
<rect x="100" y="100" width="40" height="20"
      transform="rotate(45, 120, 110)"/>   <!-- 中心 = (100+20, 100+10) -->

颜色不对 / 填充成黑色 / 描边看不见

现象 原因 解法
填充莫名是黑色 元素没写 fill,继承默认值(黑) 显式写 fill="none"(不想填充时)或指定颜色
描边看不见 只写了 stroke 但没写 stroke-width(默认可能为 1 或继承自外部) stroke-width="2"
path 被填充了一块奇怪的实心区域 pathd 没有闭合,SVG 会自动连回起点再填充 fill="none",或在 d 末尾加 Z 显式闭合
暗色主题下看不清 颜色写死了 亮暗双主题与 MkDocs 嵌入
内联后样式异常 被站点全局 CSS 影响 用更具体的选择器,或给元素加内联 style(优先级最高)

文字相关

现象 原因 解法
中文显示为方块 □□□ 渲染环境缺中文字体 指定字体族(见下方);或把文字转成路径(转曲,失去可编辑性)
文字位置偏高/偏低 y基线不是顶部 dominant-baseline="middle" 并把 y 设为目标中心
文字重叠 间距不够 y/dy、缩小 font-size、改 text-anchor(见手术五)
文字被裁掉 超出 viewBox 或被 clipPath 裁切 加大 viewBox;检查裁剪区域

指定中文字体族:

<text font-family="Noto Sans SC, Microsoft YaHei, PingFang SC, sans-serif">中文</text>

多个字体用逗号分隔,渲染时从前到后依次尝试,第一个可用的生效。末尾一定要留一个通用族(sans-serif)兜底。


箭头 / 渐变 / 滤镜不见了

现象 原因 解法
箭头消失 <defs> 里的 marker 被删了,或 marker-end="url(#id)" 的 id 拼错 搜索 url(# 核对 id 与 <defs> 中的 id 是否完全一致(含大小写)
渐变变成纯色/黑块 fill="url(#grad)" 引用的 id 不存在 同上,核对 id
滤镜效果没了 filter="url(#blur)" 引用丢失 同上
删了某个元素后,多处同时出问题 删掉的是 <defs> 里被共享引用的定义 <defs> 内容前务必先搜 id 引用

id 是全局唯一的

SVG 里 id 在整个文档内必须唯一。如果内联了多张 SVG 到同一个页面,而它们用了相同的 id(比如都叫 arrow),就会产生冲突——浏览器只认第一个,后面的引用会指向错误的对象。 做法:内联多张 SVG 时,给每张图的 id 加前缀,例如 fig1-arrowfig2-arrow


图很模糊 / 放大后有锯齿

原因 怎么确认 解法
其实是位图(PNG/JPG 套了个 .svg 后缀,或 SVG 里嵌了 <image> 位图) 放大看是否出现像素块;搜文件里有没有 <image 真正的矢量图无论放多大都是平滑的。是位图就只能换素材
显示尺寸远小于 viewBox 尺寸 检查 width/heightviewBox 的比例 按需要调整显示尺寸
启用了非整数缩放 显示宽高与 viewBox 比例不匹配 一般影响不大,可忽略

放进 MkDocs 后的问题

现象 原因 解法
图片 404 / 不显示 相对路径基准错;或手动做了 URL 编码 亮暗双主题与 MkDocs 嵌入 §路径与文件名编码
<img src> 在子页面 404(首页却正常) 用裸 HTML <img src="assets/x.svg">;MkDocs 只重写 markdown ![](x.svg) 的链接,不重写裸 HTML 的 srcuse_directory_urls 默认开启时每篇页变自身目录,子页层级下 assets/ 解析到错误目录 改用 markdown 图片语法 ![alt](assets/x.svg){: style="max-width:680px;width:100%;height:auto"},让 MkDocs 按页面深度自动重写相对路径(子页→../assets/...、首页→assets/...),与页面层级彻底解耦
构建时告警 "contains a link ... but ... not found" 路径写错 按告警里的路径去核对
本地预览正常,部署后 404 文件名大小写不一致(Windows 不敏感,Linux 服务器敏感) 统一大小写,推荐全小写
图太宽撑破页面 .svg 里写死了 width 删掉 width/height,只留 viewBox;或用 <img style="max-width:100%">
链接指向了完全无关的页面 见下方说明 链接到索引页时用站点绝对路径

在 MkDocs 里引用本地图片,优先用 ![](path) 而非裸 <img>

本站多次踩过这个坑:裸 HTML <img src="assets/x.svg">src 不会被 MkDocs 重写相对路径,而 use_directory_urls(默认 true)会让每篇页面变成自己的目录。结果——

  • 页面在顶级目录(如 SVG/index.md/SVG/)时,assets/ 碰巧解析正确,图显示正常;
  • 同一张图放在子页面(如 SVG/改图的六种常见手术.md/SVG/改图的六种常见手术/)时,assets/ 被解析到不存在的 /SVG/改图的六种常见手术/assets/,直接 404。

首页正常、子页挂掉,正是这个特征。正确写法

![图注](assets/x.svg){: style="max-width:680px;width:100%;height:auto"}

MkDocs 会按每篇页面的实际深度自动重写路径(子页生成 ../assets/x.svg、首页生成 assets/x.svg),无论页面嵌套多深都不会错;GitHub Pages 子路径部署也安全,无需写死绝对路径。

样式用 attr_list 的 {: style="..."} 携带(Material 默认开启 attr_list 扩展),等价原先 <img> 上的 style 属性。

大小写是 Windows 用户的经典坑:本机预览一切正常(Demo.svgdemo.svg 都能打开),推到 Linux 服务器上就 404。文件命名统一小写可根治。

多图并排:最简单可靠的是「同一段落内多个 inline 图」,别用 flex 容器

两张图并排,最朴素的写法是把两个 markdown 图片语法放在同一段落(中间用空格/换行分隔),各占约一半宽度——两个 <img> 是行内元素,天然左右排列,不依赖任何 HTML 容器(flex/grid 在 Typora/MkDocs 混合环境不可靠,曾导致 DW01 页两图垂直堆叠)。完整排版说明(含图注写法、为什么要 display:inline-block)见 嵌入页「多图并排与图注」

![图 A 说明](assets/a.svg){: style="display:inline-block;max-width:380px;width:47%;height:auto;vertical-align:middle" }
![图 B 说明](assets/b.svg){: style="display:inline-block;max-width:380px;width:47%;height:auto;vertical-align:middle" }
  • 两图必须在同一个 markdown 段落里(段落间空行会断开 → 变垂直);width:47% 给中间留出空隙。
  • display:inline-block 防止主题把 img 强制成块级;vertical-align:middle 让两图底部对齐。
  • 即使渲染器不支持 {: style} 属性(如部分 Typora 场景),图也会按固有尺寸并排——只是可能偏小。
  • 需要分图注时,在下方另起一段写合并说明,或用表格/图注段落,别用 <figure>+<figcaption> 包裹 markdown 图(结构不稳)。
  • 本站 实战 DW01 复盘页 曾试过 <figure markdown> + flex 容器(产物 HTML 结构正确但实际垂直)与裸 <img>(MkDocs 不重写路径 404)两套方案,最终回落到上面的 inline 写法。判断标准:构建后打开产物 HTML,两个 <img> 应位于同一个 <p>src 已被 MkDocs 按页面深度重写。

启用 wikilink 插件后,纯文件名链接会被全局匹配(很隐蔽)

许多 MkDocs 站点会装 wikilink 类插件(如 mkdocs-roamlinks-plugin),用来支持 [[双括号]] 写法。这类插件通常会把不带目录前缀的纯文件名链接也纳入"按文件名全局匹配"的逻辑。

后果:](index.md) 这种写法,本意是"当前目录下的 index.md",但插件会在整个 docs 目录里找所有叫 index.md 的文件——站点里往往有十几个——然后匹配到其中一个,最终链接指向一个毫不相关的页面。构建不报错、页面渲染正常,只是链接是错的,非常隐蔽。

判断方法:打开构建产物的 HTML,检查该链接的 href 是否指向了预期之外的目录。

规避办法

<!-- 可能被误匹配(index.md 是重名文件) -->
[← 返回索引](./index.md)

<!-- 站点绝对路径(推荐,最稳) -->
[← 返回索引](/分类/子分类/)

<!-- 带目录前缀(同样能绕开纯文件名匹配) -->
[← 返回索引](../子分类/index.md)

插件连代码块里的示例也会一起改

上面第一行示例在构建时确实会被插件改写(本站在写这一页时就遇到过)。这是纯文本替换,与是否在代码块中无关——所以不要照抄页面源码里的示例文本,按"带前缀或绝对路径"的原则自己写即可。

文件名在站内唯一时(如 改图的六种常见手术.md)不受影响,出问题的基本都是 index.md 这类通用名。


速查卡

白屏          → 先验 XML 合法性(一行 python 命令)
只显示一半    → 查 viewBox 是否够大 / 有无 clipPath
改了没变化    → 亮色试改验证找对了没;检查是否被后绘制的元素盖住
位置跑偏      → 父级 <g> 的 transform 会累加;rotate 记得给旋转中心
填充黑块      → 补 fill="none"
中文方块      → 指定中文字体族
箭头消失      → 核对 url(#id) 与 <defs> 里的 id 是否一致(含大小写)
部署后 404    → 文件名大小写 / 相对路径基准

相关:← 亮暗双主题与嵌入 · 实战复盘 → · ← SVG 动手区