Skip to main content

響應式圖片 srcset、sizes 和 picture 設定

響應式圖片的難題在於瀏覽器解析 HTML 時,圖片的預載(preload)發生在 CSS 尚未被解析、layout 尚未建立之前。因此瀏覽器在決定下載哪張圖時,根本不知道這張圖最終會被渲染成多寬。srcset 與 sizes 就是為了解決這個在 preload 階段就必須做出決策的問題而設計的。

srcset​

srcset 是一個逗號分隔的圖片候選列表,每個條目由 URL 加上 descriptor 組成,有兩種 descriptor 語法:

width descriptor(w):描述該圖片檔案本身的固有寬度(intrinsic width),單位為像素。例如 hero-800.jpg 800w 的意思是「這個檔案開啟後是 800px 寬」,這是關於檔案的事實,而非給瀏覽器的渲染指令。這種語法必須搭配 sizes 使用。

pixel density descriptor(x):描述目標裝置的像素密度倍率,例如 hero-2x.jpg 2x。這種語法只處理 DPR,無法因應 viewport 寬度變化,且不能搭配 sizes 使用(搭配時瀏覽器直接忽略 sizes)。

沒有理由用 x descriptor,因為手機通常是 high DPR,電腦通常 low,但是也可以 high,造成 x descriptor 根本沒有判斷能力。

sizes​

sizes 則是由逗號分隔的 media condition + 寬度配對列表。它的作用是代替瀏覽器無法自行取得的 CSS layout 資訊,明確告訴瀏覽器「在不同的 viewport 條件下,這張圖會被渲染成多寬」。

格式為:(media condition) width, (media condition) width, ..., default-width

  • 瀏覽器依序評估,使用第一個成立的條件對應的寬度
  • 最後一項不帶 media condition 作為預設值
  • 寬度單位支援 px、vw、em、calc(),不支援 %
  • Media condition 語法類似 CSS media query,但不支援 screen、print 等 media type,只支援條件部分

選擇流程​

理解完設定來講瀏覽器是怎麼透過這兩個設定選擇圖片的,流程如下:

  1. 評估 sizes 列表,找出第一個成立的 media condition,得到 slot 寬度(CSS pixels)
  2. 將 slot 寬度乘以裝置的 DPR(Device Pixel Ratio),得到所需的實際像素寬
  3. 從 srcset 候選清單中,以各圖的 w descriptor 值計算出對應的 effective pixel density,選出最適合的圖

WHATWG 規格與 MDN 都說到在瀏覽器在做選擇時,除了 viewport 寬度與 DPR,還可能考量縮放比例、網路速度、螢幕方向等因素,因此 srcset + sizes 給的是提示而非強制指令,瀏覽器保留最終裁量權。

實際範例​

<img
srcset="image-500.jpg 500w,
image-1000.jpg 1000w,
image-1500.jpg 1500w"
sizes="(min-width: 1280px) 416px,
(min-width: 853px) 80vw,
(min-width: 648px) 100vw,
100vw"
src="image-1000.jpg"
alt="..."
/>
  1. 以 iPhone 16 作為範例,裝置資訊: Viewport Size = 393px × 852px, DPR = 3
  2. sizes 檢查:從左到右檢查 sizes 屬性的每個條件,第一個符合的就使用:
    1. (min-width: 1280px) → 檢查 393px ≥ 1280px?否,繼續
    2. (min-width: 853px) → 檢查 393px ≥ 853px?否,繼續
    3. (min-width: 648px) → 檢查 393px ≥ 648px?否,繼續
    4. 100vw → 這是預設值,使用此項
  3. 計算實際寬度:100vw = 100vw × 393px = 393px
  4. 計算實際所需的物理像素寬度:393px × 3DPR = 1179px
  5. srcset 選取圖片:從 srcset 設定中選擇最適合的圖片
    1. 500px < 1179px ❌
    2. 1000px < 1179px ❌
    3. 1500px ≥ 1179px ✅

所以這個設定下, iPhone 16 會選到最大的圖片。

範例中 sizes 四個設定的意思是,大螢幕已經確定渲染區域就是 416px,因此固定 416px。中小螢幕需要佔據螢幕全寬才看的清楚圖片,因此設定 vw 以頁面寬度百分比作為參數。

由此可知,sizes 和 srcset 不是單純節省流量,也考慮在不同響應式視窗中使用不同的圖像佈局,單看 MDN 文檔絕對不可能正確設定,很少有文章講到這個邏輯,有沒有看懂這個邏輯會讓你的設定方式完全不同。

更多重點整理如下:

  1. 規範上 sizes 要和 srcset 同時出現才可協同運作,不過就算沒寫瀏覽器還是會自動幫你選圖片
  2. 使用 loading="lazy" 可以啟用 sizes="auto" 選項,不過就算沒寫瀏覽器也還是會幫你 lazy
  3. w descriptor 文檔說明他的值應該要和圖片寬度完全相同,但實際上不用,因此可以做一個 hack,直接不寫 sizes,並且把 w descriptor 的值改成你想要的大小,就不用絞盡腦汁考慮每個裝置對應 sizes/srcset 這個複雜的查找流程
  4. 如果只是簡單的想要在不同 CSS pixel 使用不同圖片,直接用 <picture> 標籤搭配 source,這樣超簡單

老實說個人網站用不太到響應式圖片,除非你是攝影網站或是圖片爆多的網站,不然即使兩百萬像素 (1920x1080) 的 WebP 也沒多大,而且作為用戶只想看到大圖,才不管那點流量。

picture 標籤​

<picture> 標籤提供的是藝術指導(art direction),允許在不同條件下提供不同裁切或構圖的圖片,而不只是同一張圖的不同尺寸版本。

<picture> 是一個容器,內部放一或多個 <source> 元素,最後以 <img> 結尾作為 fallback:

<picture>
<source
media="(min-width: 1280px)"
srcset="hero-desktop.jpg"
/>
<source
media="(min-width: 640px)"
srcset="hero-tablet.jpg"
/>
<img src="hero-mobile.jpg" alt="..." />
</picture>

瀏覽器從上往下依序評估每個 <source> 的 media 條件,使用第一個符合的來源。如果都不符合,或瀏覽器不支援 <picture>,就直接使用 <img> 的 src。這裡的邏輯和 sizes 完全一樣:從上到下、第一個成立為準、<img> 是最終預設。

<source> 同樣支援 srcset 與 sizes,因此可以再疊加解析度切換:

<picture>
<source
media="(min-width: 1280px)"
srcset="hero-desktop-1x.jpg 1000w, hero-desktop-2x.jpg 2000w"
sizes="1000px"
/>
<img
src="hero-mobile.jpg"
srcset="hero-mobile-500.jpg 500w, hero-mobile-1000.jpg 1000w"
sizes="100vw"
alt="..."
/>
</picture>

另一個常見用途是格式切換,提供現代格式並以舊格式保底:

<picture>
<source srcset="hero.avif" type="image/avif" />
<source srcset="hero.webp" type="image/webp" />
<img src="hero.jpg" alt="..." />
</picture>

瀏覽器會選第一個它支援的 type,不支援 AVIF 就跳到 WebP,連 WebP 都不支援才用 JPEG。

雖然 picture 標籤支援更複雜的切換,但是如果你只是要大螢幕原圖,小螢幕縮圖,那 picture 反而更簡單,用 media query 就可以輕鬆判斷。

附帶一提​

附帶一提,如果你是個人部落格,且響應式圖片的目的是「顯示剛剛好尺寸的圖」不是「大螢幕原圖,小螢幕縮圖」,那直接丟一張 1320px 的圖就好了,因為現在最大的行動裝置 CSS pixel 是 iphone 18 pro max 440px 乘以 3 DPR = 1320px,而桌面裝置顯示時通常只會佔據頁面一半,(頁面通常會有側欄加上左右留白,扣掉這些差不多只剩一半空間放主要內容),以 2K 螢幕來說 1280px 剛剛好和 1320px 差不多,直接用同一張圖片就夠了。

至於平板裝置:誰理你,我的網站平板裝置用戶 0.1% 都不到,也不會有人因為你給他剛剛好的尺寸感謝你。

再附帶一提,不要放解析度過高的圖片,純浪費流量,還會有毛邊,甚至會影響效能,也沒人會點開看你的圖片,不如提供下載原圖的方式而不是拿大圖卡爆用戶體驗,比如 A11y 新手村 和 afilmory 的範例網站都有一樣的問題,用戶連一般瀏覽要看到圖片都有問題了,那放高解析度的圖片根本沒意義。

資訊來源​

這些資料來源都是官方文件、官方介紹、技術大神說法,強烈建議看,網路上的中文資訊寫的奇差無比看了反而干擾理解。