若要移除 SVG use 實例周圍意外出現的空白,請確認空白是來自來源 viewBox、長寬比適配,還是周圍 CSS 行框。本指南說明如何診斷並修正各種成因,同時讓定位座標維持可預測。

這項任務是簡單的桌面網頁編輯:在 SVG 內精確定位可重複使用的形狀,然後讓周圍的 HTML 版面符合預期。你可以在 Mac 上使用純文字編輯器參照範例操作,再用目前版本的桌面瀏覽器開啟儲存的 HTML 檔案。

哪個座標系統控制空白?

有三個邊界需要檢查:外層 SVG 元素的 CSS 方框、被參照符號使用的視埠,以及該符號內的實際形狀。W3C 明確區分 SVG 視埠與瀏覽器的 CSS 視埠。viewBox 會將 SVG 使用者座標映射至 SVG 視埠;它描述的不是瀏覽器視窗。座標系統、轉換與單位 — SVG 2

例如,x="20" 在 use 元素上提供的是 SVG 座標系統中的位移,不代表距離瀏覽器視窗左邊緣 20 個 CSS 像素。縮放和祖先元素的轉換會改變該位移在頁面上的呈現位置。SVG 規範將 use 定位描述為額外的平移,其行為與 CSS 方框版面配置不同。文件結構 — SVG 2

先參考這份判斷表。這是一套依據文件所述版面配置機制整理的診斷流程,不是可互換使用的修正方法清單。

觀察到的現象 優先檢查項目 可能的修正方式
整個 SVG 下方出現一條窄空白 行內排版與父元素的行高 將獨立 SVG 設為區塊層級,或調整其行內對齊方式
每個重複圖形內都有空白 形狀座標與符號的 viewBox 若內縮並非預期,請收緊來源邊界
符號實例兩側多出空白 實例尺寸與 viewBox 長寬比 讓兩者長寬比相符,或選擇明確的適配方式
區域內位置正確,但頁面位置錯誤 SVG 使用者單位與瀏覽器視埠座標 指定 x 和 y 前先轉換座標
調整實例尺寸沒有作用 href 指向的元素 使用能定義視埠的符號來進行適配,或轉換幾何圖形

使用完整的本機範例重現空白

在純文字編輯器中將以下完整文件儲存為 svg-spacing.html,然後用瀏覽器開啟。範例只使用示意標籤和矩形,不需要任何函式庫或外部檔案。淺色矩形標示預期的實例視埠;深色矩形則是重複使用的圖稿。

<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <title>SVG spacing example</title>
  <style>
    body {
      margin: 24px;
      font: 16px system-ui, sans-serif;
    }
    #canvas {
      display: block;
      width: 320px;
      height: 200px;
      outline: 1px solid #687580;
      background: #fff;
    }
    .viewport-guide { fill: #e4edf2; }
    .artwork { fill: #356b8c; }
    text { fill: #253744; font-size: 14px; }
  </style>
</head>
<body>
  <p>Two instances with different source bounds and fitting dimensions.</p>
  <svg id="canvas" xmlns="http://www.w3.org/2000/svg"
       width="320" height="200" viewBox="0 0 320 200"
       role="img" aria-labelledby="drawing-title">
    <title id="drawing-title">Padded and tightly fitted rectangles</title>
    <defs>
      <symbol id="padded" viewBox="0 0 100 100">
        <rect id="source-shape" class="artwork"
              x="20" y="20" width="60" height="60" />
      </symbol>
      <symbol id="tight" viewBox="20 20 60 60">
        <rect class="artwork"
              x="20" y="20" width="60" height="60" />
      </symbol>
    </defs>

    <rect class="viewport-guide"
          x="20" y="20" width="120" height="60" />
    <use href="#padded" x="20" y="20" width="120" height="60" />
    <text x="160" y="55">Padded source</text>

    <rect class="viewport-guide"
          x="20" y="110" width="60" height="60" />
    <use id="tight-instance" href="#tight"
         x="20" y="110" width="60" height="60" />
    <text x="110" y="145">Tight source</text>
  </svg>
</body>
</html>

兩個實例都明確設定了尺寸,避免排查空白時依賴隱含尺寸。MDN 說明 use 會重複使用被參照的 SVG 內容,也指出當目標是具有 viewBox 的 SVG 或符號時,其 width 和 height 會影響適配。範例使用目前的 href 屬性。MDN:use

計算第一個矩形實際開始的位置

第一個來源的 viewBox 為 100 × 100,但其矩形只占用各方向上從 20 到 80 的座標。指定的實例視埠為 120 × 60。使用預設的 xMidYMid meet 適配方式時,縮放比例是 120 / 100 和 60 / 100 中較小的值:0.6。縮放後的 viewBox 占 60 × 60,因此左右各留出 30 個單位。這符合 W3C 的視埠轉換演算法。SVG 2:計算 SVG 視埠的等效轉換

以這些示意輸入值計算,繪製出的矩形從水平位置 20 + 30 + 20 × 0.6 = 62 和垂直位置 20 + 20 × 0.6 = 32 開始。其各方向尺寸都是 60 × 0.6 = 36。這些數值已透過簡單的確定性計算檢查;它們是計算結果,不是瀏覽器測試的測量值。

第二個實例修改了兩個互不相關的輸入值:來源邊界變為 20 20 60 60,而實例視埠變為 60 × 60。因此矩形會填滿該視埠,並從 SVG 座標 (20, 110) 開始。若要自行分辨成因,請一次只改一個輸入值。

如何移除實例內部的空白?

修正來源邊界中非預期的空白

viewBox 包含四個數值:最小 x、最小 y、寬度和高度。其原點不必是零。範例中的 viewBox="20 20 60 60" 描述矩形現有的邊界,不必重寫幾何圖形。MDN:viewBox

收緊實際圖形的邊界前,請先判斷其內縮是否為刻意設計。一組可重複使用的形狀可能會共用同一個座標框架,以確保視覺尺寸和位置一致。如果分別收緊每個符號,可能會改變這些關係。若共用邊界是設計一致性的一部分,就請保留它們。

檢查幾何圖形時,getBBox() 會回報 SVG 使用者座標中的邊界。預設情況下,它會排除筆畫和標記,也不會計入套用至目標元素或其父元素的轉換。因此,純填色的外框不足以作為裁切帶有筆畫或經過轉換圖稿的依據。MDN:SVGGraphicsElement getBBox()

選擇長寬比不符時的適配方式

即使圖稿邊界緊貼內容,將正方形來源放進寬幅視埠時,周圍仍可能出現空白。請依預期結果選擇適配方式。若要調整符號內部的適配,請對被參照的符號套用 preserveAspectRatio;若要診斷外層座標映射,則套用在外層 SVG 上。MDN:preserveAspectRatio

選項 結果與條件
來源與視埠的長寬比相符 保留原有比例,同時避免因適配長寬比產生空白
xMidYMid meet 顯示整個 viewBox 並置中;長寬比不符時會留下空白
xMinYMin meet 將適配後的 viewBox 移至靠近左上角的位置;仍會有剩餘空白
none 允許非等比縮放;正方形可能會變成矩形
xMidYMid slice 填滿視埠,但部分來源內容會超出視埠;可見範圍由裁切決定

改變對齊方式只會移動適配產生的空白,不會移除來源幾何圖形中原本就有的內縮。同樣地,若必須讓所有來源內容都留在視埠內,就不適合使用 slice。這些差異符合 MDN 對對齊方式、meet、slice 和非等比適配的定義。MDN:preserveAspectRatio

當尺寸看似無效時,檢查參照目標

單純的群組或路徑不會只因為被 use 元素參照,就取得可用於適配的視埠。W3C 指出,當被參照的元素不會建立新的視埠時,實例的 width 和 height 不會生效。若要讓每個實例都能預測地適配,請像上面的範例一樣,將可重複使用的幾何圖形放入具有明確邊界的符號中。若要直接縮放幾何圖形,請使用轉換並將其對定位造成的影響納入考量。SVG 2:重複使用圖形的版面配置

如何移除外層 SVG 下方的空白條?

SVG 外部的空白可能來自 HTML 行內排版。WHATWG 允許在片語內容中使用 SVG,因此周圍頁面可以將它與文字並排放置。CSS 行框會在基線上方和下方納入字型度量值,因此行內圖形的方框下方可能出現可見空白。HTML 標準:SVG、CSS 2:行高計算

完整範例已將 display: block 設定在 #canvas 上,讓 SVG 不再參與行內行框。若是獨立圖形,請保留這項設定。若要了解其運作方式,可暫時改為 display: inline,然後檢查周圍的行框。

若 SVG 必須與文字並排,請嘗試對外層 SVG 設定 vertical-align: bottom,再檢查文字對齊結果。這個屬性會調整行內層級方框在其行框中的位置,不會改變符號邊界或內部適配。其他內容和父元素的行高仍可能決定整個行框的高度。MDN:vertical-align

以下 CSS 宣告是外層 SVG 的替代設定,不是要貼到每個實例上的累加修正方式:

/* Standalone drawing */
#canvas {
  display: block;
}

/* Alternative when the drawing must stay inline */
/*
#canvas {
  display: inline;
  vertical-align: bottom;
}
*/

如果座標來自瀏覽器視埠,該怎麼辦?

getBoundingClientRect() 會以 CSS 像素回報元素相對於瀏覽器視埠的位置。頁面捲動時,這些數值也會改變。這些值屬於不同於 SVG 使用者座標的空間,因此通常不能直接將 left 和 top 複製到實例的 x 和 y 中。MDN:Element getBoundingClientRect()

若輸入的是瀏覽器視埠中的點,請使用座標轉換。getScreenCTM() 會提供 SVG 轉換矩陣,套用其反矩陣即可將該點轉回所選元素的本機座標。點的 matrixTransform() 方法可以執行轉換。MDN:getScreenCTM()、MDN:matrixTransform()

以下主控台程式碼是對上方完整文件的選用擴充。它會將瀏覽器視埠座標點 (100, 150) 轉換為外層 SVG 的使用者座標,並將邊界貼合內容的實例移至該處。此程式碼假設範例中未經轉換的實例仍是該 SVG 的直接子元素。

(() => {
  const canvas = document.getElementById("canvas");
  const instance = document.getElementById("tight-instance");
  if (!canvas || !instance) {
    throw new Error("Open the complete SVG spacing example first.");
  }

  const matrix = canvas.getScreenCTM();
  if (!matrix) {
    throw new Error("The SVG coordinate matrix is unavailable.");
  }

  const viewportPoint = new DOMPoint(100, 150);
  const localPoint = viewportPoint.matrixTransform(matrix.inverse());
  if (!Number.isFinite(localPoint.x) || !Number.isFinite(localPoint.y)) {
    throw new Error("The SVG coordinate matrix cannot be inverted.");
  }

  instance.setAttribute("x", String(localPoint.x));
  instance.setAttribute("y", String(localPoint.y));
})();

有限數值檢查很重要,因為不可逆矩陣會產生 NaN 分量。版面配置或捲動狀態改變後,若要轉換新的點,請重新計算矩陣。如果實例位於經過轉換的群組內,請改為轉換至該父群組的座標系統。研究期間已根據 API 文件檢查此程式碼片段,但未在瀏覽器中執行。MDN:DOMMatrixReadOnly inverse()

最後,分別檢查三個邊界:外層 SVG 方框、符號的適配視埠,以及圖稿本身的邊界。修正引入非預期空白的邊界,再確認結果符合預期頁面尺寸。如此便能讓定位維持可預測,不必用任意位移補償原因不明的空白。

來源