在 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 瀏覽器中開啟頁面,檢查顯示後的尺寸、間距和顏色。

若要變更共用圖畫,請編輯唯一的路徑定義。若要變更單一實例,請編輯其包裝元素的尺寸或繼承顏色。讓這些工作各自分明,就能重複使用圖稿,並預期各個實例會出現在指定位置。

來源