使用 picture 在檢視區寬度的斷點處切換正方形與寬幅圖片檔案。為每個來源設定各自的尺寸,並讓 CSS 按比例縮放選取的圖片。本指南會在 Mac 上建立並測試本機範例。

picture 如何在不同長寬比之間切換?

在 picture 內,將帶有 media 條件的 source 元素放在後備 img 之前。瀏覽器會選取第一個符合條件的來源,接著從該來源的 srcset 中選取圖片。這稱為藝術指導:針對不同版面提供不同構圖。HTML 標準明確支援這種做法。HTML 標準:圖片

三個部分會共同運作:來源選擇資源,尺寸屬性描述該資源的比例,而 CSS 控制其顯示大小。所選來源的 width 和 height 可提供內含圖片所使用的長寬比。picture 元素本身不會繪製圖片;它的 img 才會。HTML 標準:嵌入內容

請依照所需結果選擇機制:

需求 適用機制
在斷點處變更構圖或長寬比 使用 picture,搭配採用 media 的 source
提供相同構圖的不同解析度 使用 srcset;搭配寬度描述符時使用 sizes
保留單一檔案,但在固定框架內裁切 CSS 尺寸設定與 object-fit: cover

這些機制處理的是不同決策。藝術指導決定要顯示哪種構圖。解析度選擇讓瀏覽器在同一構圖中選擇合適的候選項目。CSS 填入方式決定所選資源如何佔據其框架。MDN 說明了 picture 的這些用途,以及它與 srcset 和填入屬性的關係。MDN:picture 元素

在 Mac 上建立完整的本機範例

此範例使用兩個原始 SVG 資源,分別包含一個橙色圓形和一個藍綠色圓角矩形。正方形版本將它們上下排列;寬幅版本則將它們並排。所有檔案都放在同一個資料夾中,切換時不需要外部資產或 JavaScript。

撰寫資源的規格很簡單:檢視區寬度小於 700px 時,使用 600 × 600 的正方形資源;寬度達 700px 以上時,使用 1200 × 600 的寬幅資源。顯示的圖片會填滿左右各留 16px 的內容區域,最大寬度為 960px。這些尺寸與斷點都是示範用的設計選擇。

儲存兩個圖形資源

建立名為 picture-demo 的資料夾。使用純文字程式碼編輯器,在資料夾中建立以下兩個檔案,並保留其確切檔名與副檔名。每個圍欄區塊都包含完整檔案;複製內容時請勿包含 Markdown 圍欄標記。

將以下內容儲存為 shapes-square.svg:

<svg xmlns="http://www.w3.org/2000/svg" width="600" height="600" viewBox="0 0 600 600">
  <rect width="600" height="600" fill="#fff5df"/>
  <circle cx="300" cy="180" r="100" fill="#de7b35"/>
  <rect x="150" y="340" width="300" height="160" rx="40" fill="#247d83"/>
</svg>

將以下內容儲存為 shapes-wide.svg:

<svg xmlns="http://www.w3.org/2000/svg" width="1200" height="600" viewBox="0 0 1200 600">
  <rect width="1200" height="600" fill="#fff5df"/>
  <circle cx="320" cy="300" r="180" fill="#de7b35"/>
  <rect x="650" y="150" width="400" height="300" rx="60" fill="#247d83"/>
</svg>

儲存 HTML 頁面

在同一個資料夾中建立 index.html。如果使用 TextEdit,請選擇 檔案 > 新增,再選擇 格式 > 製作純文字,然後貼上程式碼。以 .html 副檔名儲存檔案;如果出現提示,請選擇 使用 .html。Apple 說明了建立 HTML 檔案的這項流程。Apple:在 Mac 上使用 TextEdit 處理 HTML 文件

<!doctype html>
<html lang="en">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>Shape study</title>
  <style>
    body { margin: 0; font-family: system-ui, sans-serif; }
    main { width: calc(100% - 32px); max-width: 960px; margin: 24px auto; }
    picture { display: block; }
    .study-image { display: block; width: 100%; height: auto; }
  </style>
</head>
<body>
  <main>
    <h1>Shape study</h1>
    <picture>
      <source media="(min-width: 700px)" srcset="shapes-wide.svg"
              width="1200" height="600">
      <img class="study-image" src="shapes-square.svg"
           width="600" height="600"
           alt="An orange circle and a teal rounded rectangle on cream.">
    </picture>
    <p>Resize the viewport to compare the two arrangements.</p>
  </main>
</body>
</html>

在 Safari 中開啟 index.html。檢視區夠寬時,圖形應該會並排顯示;檢視區較窄時,圖形應該會上下排列。請將三個檔案放在一起:兩個相對資源 URL 會以 HTML 檔案所在位置為基準解析。

寬幅來源使用 srcset,即使它只有一個 URL 也是如此。在 picture 內,source 必須搭配 srcset;不能使用它的 src 屬性。尺寸屬性是沒有單位的整數,因此要使用 width="1200",而不是 width="1200px"。MDN:source 元素

為什麼要同時在來源和後備項目上宣告尺寸?

後備項目的 600 × 600 尺寸描述正方形資源。寬幅來源的 1200 × 600 尺寸則描述寬幅資源。提供這兩組尺寸後,支援此功能的瀏覽器可以在下載圖片前先確定所選圖片的比例。若寬幅來源沿用正方形尺寸,初始長寬比就會不正確。HTML 標準:來源尺寸屬性

接著,CSS 會將圖片寬度設為可用內容區域的寬度,並讓高度保持自動。自動高度可讓圖片比例決定其高度。CSS 尺寸規格草案說明了偏好長寬比如何參與自動尺寸計算;明確固定兩個尺寸則會失去這種彈性。CSS 盒子尺寸模組第 4 級

此範例可透過以下簡單計算檢查版面:

圖片寬度 = min(檢視區寬度 - 32, 960)
正方形高度 = 圖片寬度
寬幅高度 = 圖片寬度 / 2
CSS 像素的檢視區寬度 選取的資源 計算出的圖片尺寸
500 shapes-square.svg 468 × 468
699 shapes-square.svg 667 × 667
700 shapes-wide.svg 668 × 334
1000 shapes-wide.svg 960 × 480

這些是根據所提供 CSS 計算出的預期值,假設捲軸寬度不會減少可用內容區域。SVG 語法、資源名稱、宣告尺寸與算術都已透過程式檢查;編寫期間並未在瀏覽器中實際呈現此範例。

請留意 699px 與 700px 之間高度的大幅變化。這是從正方形構圖切換到寬幅構圖的直接結果。調整大小時出現這種變化是預期現象。如果檢視區固定不動,載入後又出現第二次跳動,就應檢查宣告尺寸以及是否有 CSS 規則覆寫它們。

在 Safari 中精確測試斷點

Safari 的「回應式設計模式」可讓你獨立於桌面視窗調整頁面檢視區大小。在 Safari 的「進階」設定中,啟用「顯示網頁開發者功能」。開啟範例後,選擇 開發 > 進入回應式設計模式,或按下 Control-Command-R。WebKit 說明了此設定、快速鍵和可調整大小的預覽畫面。WebKit:簡化的回應式設計模式

先測試 699px 和 700px,接著測試一個足夠窄和一個足夠寬的檢視區。確認構圖在預定邊界切換、橙色圖形維持圓形,而且圖片下方的段落會配合圖片高度變化。分別在斷點兩側重新載入,檢查初始版面與調整大小時的結果。

若要直接確認 Safari 選取了哪個檔案,可在檢視範例時,於 Web Inspector 的主控台執行以下選用的診斷程式碼:

const studyImage = document.querySelector('.study-image');
if (!studyImage) {
  throw new Error('Open the Shape study example before running this snippet.');
}
const studyBox = studyImage.getBoundingClientRect();
console.table({
  selectedURL: studyImage.currentSrc,
  displayedWidth: studyBox.width,
  displayedHeight: studyBox.height
});

跨越斷點後再次執行程式碼。選取的 URL 應以對應的正方形或寬幅檔名結尾。currentSrc 屬性會回報瀏覽器選擇的 URL;單靠它的值無法證明資源已成功載入。MDN:HTMLImageElement currentSrc 屬性

切換結果不如預期時要檢查什麼?

依序檢查選取結果、資源尺寸和呈現方式。這樣可以避免把 CSS 尺寸問題誤認為資源選取問題。

  1. 寬螢幕下仍選取正方形檔案。 確認檢視區達到 700px、source 排在 img 之前,而且來源使用 srcset。有多個來源時,請檢查它們的順序:較早出現且符合條件的來源可能會優先採用。HTML 標準:藝術指導選取
  2. 資源沒有顯示。 比對標記中的檔名與資料夾內的檔案,包括副檔名。此範例的兩個 SVG 檔案都必須放在 index.html 旁邊。
  3. 顯示了正確資源,但形狀錯誤。 檢查套用於實際 img 的 CSS,尤其是指定固定高度的規則。要按比例調整尺寸,請恢復 height: auto。
  4. 圖片載入後跳動。 比較每組宣告尺寸與資源的實際比例。也請檢查是否有明確的 CSS aspect-ratio 覆寫了預期比例。

如果實際設計需要固定框架,請慎選填入方式。object-fit: contain 會保留完整資源,可能留下空白;cover 會填滿框架,也可能裁切內容;fill 則可能拉伸內容。請將填入屬性套用於 img。此範例不需要固定框架,因為版面會隨所選資源的比例調整。MDN:object-fit

瀏覽器支援有哪些限制?

對 picture 的支援與對來源專屬尺寸的支援,是兩個不同的考量。HTML 標準的相容性摘要列出目前瀏覽器引擎中的 picture。MDN 將反映尺寸屬性的來源 width 屬性標示為自 2022 年 12 月起已獲各瀏覽器支援。不可假設舊版瀏覽器能正確保留來源專屬尺寸。HTML 標準:picture 元素、MDN:HTMLSourceElement width 屬性

如果沒有來源符合條件,或瀏覽器不支援 picture,後備 img 會提供圖片。此範例使用的是正方形構圖。能切換資源、但不支援來源尺寸的瀏覽器,可能要等圖片載入後才能確定寬幅圖片的最終比例。MDN:picture 元素

如果目標瀏覽器支援 CSS aspect-ratio,但需要明確的尺寸規則,請將以下 CSS 片段附加到現有樣式表。它會比照 HTML 斷點,提供暫定比例,同時允許載入圖片後改用其自然比例:

.study-image {
  aspect-ratio: auto 1 / 1;
}

@media (min-width: 700px) {
  .study-image {
    aspect-ratio: auto 2 / 1;
  }
}

MDN 說明了 auto 與比例搭配使用的方式,並列出自 2021 年 9 月起已獲各瀏覽器支援的 CSS aspect-ratio。因此,此片段也有自身的支援範圍。請讓片段中的斷點與來源條件保持一致,並測試你實際需要支援的瀏覽器版本。MDN:aspect-ratio

範例正常運作後,請將合成檔案換成自己的構圖,並一併更新尺寸。保持來源條件、資源比例與 CSS 尺寸設定一致,然後在斷點正下方與正上方重新執行檢查。

來源