在 SVG <symbol> 中定义一次图形,然后在每个可见 SVG 中使用 <use> 引用其 ID。为每个实例设置自己的尺寸,并让 CSS 排列页面中的其他内容。SVG 文档结构。

本教程适合在 Mac 上编辑 HTML 页面的用户,他们希望在多个文本标签旁显示同一个小图形。完整示例使用合成的色块、普通 HTML 和 CSS。无需框架、JavaScript、下载的图稿或构建步骤。

一个符号如何生成多个可见图形?

<symbol> 是一个图形模板。它本身不会显示;<use> 引用会创建一个可见实例。在文档中只放置一次定义,然后在图形需要出现的位置添加一个小型 SVG 包装元素。MDN 文档指出,这两个元素都已广泛支持。MDN:symbol。

关键区别在于,避免重复编写路径,不等于避免浏览器执行渲染工作。HTML 中只有一个路径定义,但浏览器仍会为每个 <use> 创建一个渲染实例。MDN 将其描述为克隆到一个不可公开访问的 DOM 中。复用让源代码更易于维护;这并不意味着多个可见图形会变成一个渲染对象。MDN:use。

对于带有标签和图标的页面,将标签保留在 HTML 中,并为每个图标提供自己的 SVG 包装元素。WHATWG 将 SVG 视为 HTML 中嵌入式、短语级和流式内容,因此可以将这些包装元素放在普通页面内容之间。HTML 标准:SVG。

创建一个含有一次定义和三个实例的完整页面

在 Mac 上用纯文本编辑器创建一个名为 svg-reuse.html 的文件。粘贴下面的完整文档,保存,然后用桌面浏览器打开。保留 .html 扩展名,并保存为纯文本,而不是富文本文件。

示例使用单个 <path> 定义了两个对角排列的色块。三个引用以不同的尺寸和颜色显示相同的几何图形。每个引用都使用 href="#demo-tiles";普通的 href 是当前写法,而 xlink:href 已弃用。MDN:use 属性。

<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>Reusable tile symbol</title>
  <style>
    body {
      margin: 2rem;
      font-family: system-ui, sans-serif;
      color: #263445;
    }
    .symbol-bank {
      position: absolute;
      width: 0;
      height: 0;
      overflow: hidden;
    }
    .samples {
      display: flex;
      flex-wrap: wrap;
      gap: 1.5rem;
    }
    .sample {
      display: flex;
      align-items: center;
      gap: 0.75rem;
      padding: 1rem;
      border: 1px solid #cbd5df;
    }
    .icon {
      display: block;
      width: 24px;
      height: 24px;
      flex: none;
    }
    .icon-large { width: 48px; height: 48px; }
    .icon-medium { width: 32px; height: 32px; }
    .blue { color: #2457a7; }
    .purple { color: #7140a0; }
  </style>
</head>
<body>
  <svg class="symbol-bank" width="0" height="0" aria-hidden="true">
    <symbol id="demo-tiles" viewBox="0 0 24 24"
            preserveAspectRatio="xMidYMid meet">
      <path fill="currentColor"
            d="M4 4H10V10H4Z M14 14H20V20H14Z"></path>
    </symbol>
  </svg>

  <main>
    <h1>Tile samples</h1>
    <div class="samples">
      <div class="sample">
        <svg class="icon" width="24" height="24"
             viewBox="0 0 24 24" aria-hidden="true">
          <use href="#demo-tiles" width="24" height="24"></use>
        </svg>
        <span>Small tiles</span>
      </div>

      <div class="sample blue">
        <svg class="icon icon-large" width="48" height="48"
             viewBox="0 0 24 24" aria-hidden="true">
          <use href="#demo-tiles" width="24" height="24"></use>
        </svg>
        <span>Large tiles</span>
      </div>

      <div class="sample purple">
        <svg class="icon icon-medium" width="32" height="32"
             viewBox="0 0 24 24" aria-hidden="true">
          <use href="#demo-tiles" width="24" height="24"></use>
        </svg>
        <span>Medium tiles</span>
      </div>
    </div>
  </main>
</body>
</html>

预期结果是三个带边框的示例,其中包含相同的对角色块。第一个使用正文文本颜色,第二个为蓝色,第三个为紫色。它们的 SVG 方框边长分别为 24、48 和 32 CSS 像素。缩窄浏览器窗口,检查这些示例如何换行。

这里的可见形状是装饰性的,因为相邻标签已经提供了内容。因此 SVG 包装元素使用了 aria-hidden="true"。这样会将它们从无障碍树中移除,但不会将其视觉隐藏。此处理方式适用于装饰性图形;不要将它用于承载文本未提供信息的图像。MDN:aria-hidden。

哪些设置控制页面布局和 SVG 布局?

这里有三项独立任务:排列 HTML 组件、设置每个 SVG 方框的尺寸,以及将图形映射到该方框中。更改其中一项的设置,可能不会影响另外两项。

任务 示例中的设置 要更改的内容
在页面上排列示例 .samples 使用 flexbox 并允许换行 在此更改间距或换行方式
让每个图形与其标签对齐 .sample 使用 flexbox 和 align-items: center 在此更改组件的对齐方式
设置 SVG 的显示方框尺寸 .icon 及其尺寸类 在此更改 CSS 宽度和高度
定义图形坐标 viewBox="0 0 24 24" 图稿的坐标边界发生变化时在此更改
在内部设置所引用符号的尺寸 <use> 的宽度和高度为 24 在此示例中,让这两个值与内部坐标区域保持一致
按比例适配图形 preserveAspectRatio="xMidYMid meet" 仅在需要采用不同适配行为时更改

Flexbox 用于排列弹性容器的直接子元素。因此,外层容器排列各个示例,每个内层容器则将 SVG 放在标签旁边。Flexbox 一次沿一个维度工作;换行后的各行会分别分配空间。如果设计需要协调行与列的布局,可以使用 CSS Grid 作为页面布局方案。MDN:Flexbox 基本概念。

在 SVG 内部,<use> 使用的是另一种布局模型。它的 x 和 y 值会在 SVG 坐标系中偏移图形,不会设置 HTML 组件之间的间距。它的宽度和高度可以设置所引用符号的视口尺寸;但对于直接引用的路径或组,这并不是通用的缩放控制方式。W3C:重用图形的布局。

为什么较大的实例仍使用 24 的内部尺寸?

外层 SVG 的 viewBox 描述了一个 24×24 的坐标区域。它的 CSS 方框可以是 48×48 像素,而内部坐标保持不变。<use> 填满这个内部 24×24 区域,然后外层 SVG 将其映射到更大的显示方框。MDN 将 viewBox 定义为四个数值:最小 x、最小 y、宽度和高度。MDN:viewBox。

以这幅具体图稿为例进行说明性计算:一个 48 像素的方框会将 24 个坐标单位映射为 48 / 24 = 2 的缩放比例。色块几何图形的坐标范围是 4 到 20,因此整体着色范围变为 (20 - 4) × 2 = 32 像素。其余空间来自图形内部有意留出的边距。

这说明,增大图标的 CSS 宽度,并不一定会让着色形状填满整个宽度。在调整页面边距进行补偿之前,先检查几何图形在 viewBox 内的边界。

矩形 SVG 方框中会发生什么?

使用 xMidYMid meet 时,图形会按统一比例缩放、保持居中,并完整适配到可用视口中。因此,方形图形放在宽矩形中时,两侧会留出空间。none 允许图形变形;slice 会保持比例,但允许 viewBox 的部分区域延伸到视口之外。若要完整显示整个图形,请使用 meet。MDN:preserveAspectRatio。

如何为每个实例设置不同的颜色?

路径使用了 fill="currentColor"。这个关键字会采用元素的 color 值,因此同一幅图形可以跟随周围文本的颜色。在示例中,更改 .blue 或 .purple 规则会同时改变标签和色块的颜色。MDN:currentColor 关键字。

这是通过继承在每个实例中实现的。SVG 的复用模型会让生成内容继承 <use> 宿主元素的样式。直接在源路径上声明的固定颜色优先级高于从父元素继承的填充色。如果需要为每个实例单独着色,请避免在共享几何图形中设置固定颜色。W3C:样式作用域与继承。

不要依赖 .blue path 之类的选择器,通过可见包装元素去选取生成的路径。生成的内容属于复用影子树,因此它与祖先元素的关系不同于普通嵌套 HTML。请在包装元素或其容器上设置 color,并在源几何图形中保留 currentColor。W3C:样式作用域与继承。

复用符号显示不正确时该检查什么?

从引用处开始,逐步向外检查。这个顺序可以区分图形缺失和尺寸或位置不正确的问题,让每项调整都针对具体机制。

  1. 检查目标。 确认 href="#demo-tiles" 与定义中的 id="demo-tiles" 相符。确保该 ID 在文档树中唯一。W3C:id 属性。
  2. 检查可见包装元素的尺寸。 仅在定义容器上保留零尺寸的 .symbol-bank 类。每个可见实例都需要非零方框尺寸,如 .icon 所示。
  3. 检查坐标边界。 确认图稿位于声明的 viewBox 范围内。意外的空白可能来自未使用的图形坐标。MDN:viewBox。
  4. 检查所引用的元素类型。 如果将符号换成直接引用路径,更改 <use> 的宽度和高度就不会以相同方式缩放它。此模式下,请恢复符号及其 viewBox。MDN:use。
  5. 检查颜色声明。 在添加更多实例选择器之前,先检查源图形中是否有固定颜色。
  6. 检查页面容器。 如果图形渲染正确,但离标签太远,请调整组件的 CSS 间距或对齐方式。MDN:Flexbox 基本概念。

将示例整合到现有页面时,只插入一次符号存储区,合并 CSS,并仅将可见 SVG 包装元素复制到其他组件。这个小型文档已通过结构检查,确认元素正确配对、ID 唯一,并且三个引用都指向同一个路径定义;这些检查无法证明浏览器渲染正常。请在计划支持的 Mac 浏览器中打开页面,检查最终尺寸、间距和颜色。

要修改共享图形,请编辑唯一的路径定义。要修改某个实例,请编辑其包装元素的尺寸或继承的颜色。明确区分这些职责,可以复用图稿,并能可靠地控制每个实例的显示位置。

来源