6 个常见的 SVG 错误(以及如何修复它们)

Avatar of Mariana Beldi
Mariana Beldi

DigitalOcean 为您旅程的每个阶段提供云产品。立即开始使用 200 美元的免费积分!

最近有人问我如何处理内联 SVG 的调试。因为它属于 DOM 的一部分,所以我们可以在任何浏览器的 DevTools 中检查任何内联 SVG。因此,我们能够对事物进行范围界定,并发现任何潜在问题或优化 SVG 的机会。

但有时,我们根本无法看到我们的 SVG。在这些情况下,在进行调试时,我会寻找六个特定方面。

1. viewBox

在使用 SVG 时,viewBox 是一个常见的混淆点。从技术上讲,在没有它的情况下使用内联 SVG 也没问题,但我们会失去它最重要的优势之一:随容器缩放。同时,如果配置不当,它可能会对我们不利,导致出现不需要的裁剪。

当元素被裁剪时,它们仍然存在——只是在我们看不到的坐标系的一部分中。如果我们在某些图形编辑程序中打开该文件,它可能看起来像这样

Cat line art with part of the drawing outside the artwork area in Illustrator.
在 Illustrator 中打开的 SVG 的屏幕截图。

最简单的解决方法是什么?在 SVG 中添加 overflow="visible",无论是在我们的样式表中、内联在 style 属性中还是直接作为 SVG 表示属性。但是,如果我们还将 background-color 应用于 SVG 或周围有其他元素,则外观可能会略有偏差。在这种情况下,最佳选择是编辑 viewBox 以显示隐藏的坐标系部分。

演示应用 overflow="hidden" 和编辑 viewBox。

在讨论 viewBox 的同时,还有一些其他值得关注的事项。

viewBox 是如何工作的?

SVG 是一个无限画布,但我们可以通过视口和 viewBox 控制我们看到的内容以及如何看到它。

视口是在无限画布上的一个窗口框架。它的尺寸由 widthheight 属性定义,或者在 CSS 中使用相应的 widthheight 属性定义。我们可以指定任何我们想要的长度单位,但如果我们提供无单位的数字,则默认为像素。

viewBox 由四个值定义。前两个是左上角的起始点(xy 值,允许使用负数)。我正在编辑这些值以重新构图。后两个是视口中坐标系的宽度和高度——在这里我们可以编辑网格的比例(我们将在关于缩放的部分中详细介绍)。

以下简化的标记显示了 SVG viewBox 和在 <svg> 上同时设置的 widthheight 属性

<svg viewBox="0 0 700 700" width="700" height="700">
  <!-- etc. -->
</svg>

重新构图

所以,这

<svg viewBox="0 0 700 700">

…映射到这

<svg viewBox="start-x-axis start-y-axis width height">

我们看到的视口从 x 轴上的 0 和 y 轴上的 0 相交处开始。

通过更改这

<svg viewBox="0 0 700 700">

…到这

<svg viewBox="300 200 700 700">

…宽度和高度保持不变(每个 700 个单位),但坐标系的起点现在位于 x 轴上的 300 点和 y 轴上的 200 点。

在下面的视频中,我向 SVG 添加了一个红色的 <circle>,其中心位于 x 轴上的 300 点和 y 轴上的 200 点。请注意,将 viewBox 坐标更改为相同的值也会将圆圈的位置更改到框架的左上角,而 SVG 的渲染大小保持不变(700×700)。我所做的只是使用 viewBox “重新构图”。

缩放

我们可以更改 viewBox 内的后两个值以放大或缩小图像。值越大,要适应视口的 SVG 单位就越多,从而导致图像变小。如果我们想要保持 1:1 的比例,则我们的 viewBox 宽度和高度必须与我们的视口宽度和高度值匹配。

让我们看看在 Illustrator 中更改这些参数时会发生什么。画板是 viewport,由一个白色的 700px 正方形表示。该区域之外的所有内容都是我们的无限 SVG 画布,默认情况下会被裁剪。

下面的图 1 显示了一个蓝色点,它位于 x 轴上的 900 和 y 轴上的 900。如果我将后两个 viewBox 值从 700 更改为 900,如下所示

<svg viewBox="300 200 900 900" width="700" height="700">

…那么蓝色点几乎完全回到了视野中,如下面的图 2 所示。我们的图像已缩小,因为我们增加了 viewBox 值,但 SVG 的实际宽度和高度尺寸保持不变,并且蓝色点回到了更靠近未裁剪区域的位置。

Figure 1.
图 1
图 2

有一个粉红色的正方形作为网格如何缩放以适应视口的证据:单位变小,并且更多网格线适合相同的视口区域。您可以在以下 Pen 中使用相同的值来查看其工作原理。

2. 缺少 widthheight

在调试内联 SVG 时,我还会查看的另一个常见问题是标记中是否包含 widthheight 属性。在许多情况下,这没什么大不了的,除非 SVG 位于具有绝对定位或弹性容器的容器内(因为 Safari 使用 0px 而不是 auto 计算 SVG width 值)。在这些情况下,排除 widthheight 会阻止我们看到完整的图像,正如我们可以通过打开此 CodePen 演示并在 Chrome、Safari 和 Firefox 中进行比较(点击图像以查看更大的视图)看到的那样。

Chrome
Safari
Firefox

解决方案?添加宽度或高度,无论是作为表示属性、内联在 style 属性中还是在 CSS 中。避免单独使用高度,尤其是在将其设置为 100%auto 时。另一种解决方法是设置 right left

您可以使用以下 Pen并结合不同的选项。

3. 无意中的 fillstroke 颜色

也可能是我们正在将颜色应用于 <svg> 标签,无论是内联样式还是来自 CSS。这很好,但标记或样式中可能还有其他与 <svg> 上设置的颜色冲突的颜色值,导致部分内容不可见。

因此,我倾向于在 SVG 的标记中查找 fillstroke 属性并将其删除。以下视频显示了一个我在 CSS 中使用红色 fill 进行样式化的 SVG。在几个地方,SVG 的部分直接在标记中以白色填充,我将其删除以显示缺失的部分。

4. 缺少 ID

这可能看起来非常明显,但你会惊讶地发现我经常看到它出现。假设我们在 Illustrator 中制作了一个 SVG 文件,并且非常仔细地命名了图层,以便在导出文件时获得很好的匹配 ID。并且假设我们计划通过挂钩到这些 ID 在 CSS 中为该 SVG 设置样式。

这是一种很好的处理方式。但是,我见过很多次将同一个 SVG 文件第二次导出到同一个位置,而 ID 却不同,通常是在直接复制/粘贴矢量时发生这种情况。也许添加了一个新图层,或者其中一个现有图层被重命名了,或者其他什么原因。无论如何,CSS 规则不再与 SVG 标记中的 ID 匹配,导致 SVG 的渲染效果与预期不同。

Underscores with numbers after the element IDs
将 Illustrator 导出的 SVG 文件粘贴到 SVGOMG 中。

在大型 SVG 文件中,我们可能很难找到这些 ID。此时,打开 DevTools,检查图形中不正常的部分,看看这些 ID 是否仍然匹配是一个好方法。

因此,我建议在交换任何内容之前,在代码编辑器中打开导出的 SVG 文件,并将其与原始文件进行比较。像 Illustrator、Figma 和 Sketch 这样的应用程序很智能,但这并不意味着我们不需要对它们进行审查。

5. 修剪和蒙版的检查清单

如果 SVG 被意外修剪,并且 viewBox 检查结果正常,我通常会查看 CSS 中是否存在可能干扰图像的 clip-pathmask 属性。查看内联标记很容易让人分心,但要记住,SVG 的样式可能在其他地方设置。

CSS 修剪和蒙版 允许我们“隐藏”图像或元素的部分内容。在 SVG 中,<clipPath> 是一种矢量操作,可以剪切图像的部分内容,没有中间结果。<mask> 标签是一种像素操作,允许透明度、半透明效果和模糊边缘。

这是一个用于调试涉及修剪和蒙版情况的小型检查清单

  • 确保修剪路径(或蒙版)和图形相互重叠。重叠的部分是显示的部分。
  • 如果有一个复杂的路径没有与您的图形相交,请尝试应用转换,直到它们匹配。
  • 即使 <clipPath><mask> 没有渲染,您仍然可以使用 DevTools 检查内部代码,所以请使用它!
  • 复制 <clipPath><mask> 内部的标记,并将其粘贴到 </svg> 标签之前。然后为这些形状添加一个 fill,并检查 SVG 的坐标和尺寸。如果仍然看不到图像,请尝试向 <svg> 标签添加 overflow="hidden"
  • 检查是否为 <clipPath><mask> 使用了**唯一的** ID,以及是否将相同的 ID 应用于被修剪或蒙版的形状或形状组。不匹配的 ID 将破坏外观。
  • 检查 <clipPath><mask> 标签之间的标记中是否存在错别字。
  • fillstrokeopacity 或应用于 <clipPath> 内元素的其他样式都是无用的——唯一有用的部分是这些元素的填充区域几何形状。这就是为什么如果使用 <polyline>,它将表现为 <polygon>,而如果使用 <line>,则不会看到任何修剪效果。
  • 如果在应用 <mask> 后看不到图像,请确保蒙版内容的 fill 不是纯黑色。蒙版元素的亮度决定了最终图形的不透明度。您将能够透过较亮的部分看到,而较暗的部分将隐藏图像的内容。

您可以在这个 Pen中体验蒙版和修剪元素。

6. 命名空间

您知道 SVG 是一种基于 XML 的标记语言吗?是的,它是!SVG 的命名空间设置在 xmlns 属性上。

<svg xmlns="http://www.w3.org/2000/svg">
  <!-- etc. -->
</svg>

关于 XML 中的命名空间有很多需要了解的,MDN 对此有一个很好的入门介绍。简单来说,命名空间为浏览器提供上下文,告知它该标记特定于 SVG。其思想是,当同一个文件中存在多种类型的 XML(如 SVG 和 XHTML)时,命名空间有助于防止冲突。这在现代浏览器中是一个不太常见的问题,但可能有助于解释旧版浏览器或像 Gecko 这样在定义文档类型和命名空间时非常严格的浏览器中出现的 SVG 渲染问题。

SVG 2 规范不要求在使用 HTML 语法时使用命名空间。但是至关重要如果支持旧版浏览器是优先事项——此外,添加它也没有任何坏处。这样,当 <html> 元素的 xmlns 属性被定义时,它在那些罕见的情况下就不会发生冲突。

<html lang="en" xmlns="http://www.w3.org/1999/xhtml">
  <body>
    <svg xmlns="http://www.w3.org/2000/svg" width="700px" height="700px">
      <!-- etc. -->
    </svg>
  </body>
</html>

这在 CSS 中使用内联 SVG 时也适用,例如将其设置为背景图像。在下面的示例中,在成功验证后,输入框后面会出现一个复选标记图标。CSS 看起来是这样的

textarea:valid {
 background: white url('data:image/svg+xml,\
    <svg xmlns="http://www.w3.org/2000/svg" width="26" height="26">\
    <circle cx="13" cy="13" r="13" fill="%23abedd8"/>\
    <path fill="none" stroke="white" stroke-width="2" d="M5 15.2l5 5 10-12"/>\
    </svg>') no-repeat 98% 5px;
}

当我们删除 background 属性中 SVG 内部的命名空间时,图像就会消失。

另一个常见的命名空间前缀是 xlink:href。当引用 SVG 的其他部分(如:图案、滤镜、动画或渐变)时,我们经常使用它。建议开始将其替换为 href,因为另一个自 SVG 2 以来已被弃用,但可能会与旧版浏览器存在兼容性问题。在这种情况下,我们可以同时使用两者。如果您仍在使用 xlink:href,请记住包含命名空间 xmlns:xlink="http://www.w3.org/1999/xlink"

提升你的 SVG 技能!

我希望这些技巧能帮助您节省大量时间,如果您发现自己正在排查渲染不正确的内联 SVG。这些只是我寻找的东西。也许您有不同的危险信号需要关注——如果有,请在评论中告诉我!

最重要的是,至少要对SVG 的各种使用方法有一个基本的了解。 CodePen Challenges 经常包含 SVG 并提供良好的练习。以下是一些提升技能的资源

有几个人我建议关注,以获取与 SVG 相关的优质内容