響應式圖片 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,只支援條件部分
選擇流程
理解完設定來講瀏覽器是怎麼透過這兩個設定選擇圖片的,流程如下:
- 評估
sizes列表,找出第一個成立的 media condition,得到 slot 寬度(CSS pixels) - 將 slot 寬度乘以裝置的 DPR(Device Pixel Ratio),得到所需的實際像素寬
- 從
srcset候選清單中,以各圖的wdescriptor 值計算出對應的 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="..."
/>
- 以 iPhone 16 作為範例,裝置資訊:
Viewport Size= 393px × 852px,DPR= 3 - sizes 檢查:從左到右檢查 sizes 屬性的每個條件,第一個符合的就使用:
- (min-width: 1280px) → 檢查 393px ≥ 1280px?否,繼續
- (min-width: 853px) → 檢查 393px ≥ 853px?否,繼續
- (min-width: 648px) → 檢查 393px ≥ 648px?否,繼續
- 100vw → 這是預設值,使用此項
- 計算實際寬度:
100vw = 100vw × 393px = 393px - 計算實際所需的物理像素寬度:
393px × 3DPR = 1179px - srcset 選取圖片:從 srcset 設定中選擇最適合的圖片
- 500px < 1179px ❌
- 1000px < 1179px ❌
- 1500px ≥ 1179px ✅
所以這個設定下, iPhone 16 會選到最大的圖片。
範例中 sizes 四個設定的意思是,大螢幕已經確定渲染區域就是 416px,因此固定 416px。中小螢幕需要佔據螢幕全寬才看的清楚圖片,因此設定 vw 以頁面寬度百分比作為參數。
由此可知,sizes 和 srcset 不是單純節省流量,也考慮在不同響應式視窗中使用不同的圖像佈局,單看 MDN 文檔絕對不可能正確設定,很少有文章講到這個邏輯,有沒有看懂這個邏輯會讓你的設定方式完全不同。
更多重點整理如下:
- 規範上 sizes 要和 srcset 同時出現才可協同運作,不過就算沒寫瀏覽器還是會自動幫你選圖片
- 使用
loading="lazy"可以啟用sizes="auto"選項,不過就算沒寫瀏覽器也還是會幫你 lazy - w descriptor 文檔說明他的值應該要和圖片寬度完全相同,但實際上不用,因此可以做一個 hack,直接不寫 sizes,並且把 w descriptor 的值改成你想要的大小,就不用絞盡腦汁考慮每個裝置對應 sizes/srcset 這個複雜的查找流程
- 如果只是簡單的想要在不同 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 的範例網站都有一樣的問題,用戶連一般瀏覽要看到圖片都有問題了,那放高解析度的圖片根本沒意義。
資訊來源
這些資料來源都是官方文件、官方介紹、技術大神說法,強烈建議看,網路上的中文資訊寫的奇差無比看了反而干擾理解。
- 使用 srcset 的響應式圖片
- 圖片成效
- Using responsive images in HTML So, with these attributes in place, the browser will: (1) Look at screen size, pixel density, zoom level, screen orientation, and network speed. (2) Work out which media condition in the sizes list is the first one to be true. (3) Look at the slot size given to that media query. (4) Load the image referenced in the srcset list that has the same size as the slot. If there isn't an exact match for the display size, the browser will choose the first image that is bigger than the chosen slot size and scale it down to fit.
- HTMLImageElement: sizes property Many displays, if not most, have a device pixel ratio (DPR) greater than one. In order to render a sharp image at the physical pixel density of the display, a browser will multiply the matched sizes hint by the DPR before selecting from srcset. For example, on a 2× display with a viewport of ~500px, the matched hint is 600px, but the browser looks for a ~1200px image and selects 1200.png as the closest available size and then scales it to fit in the available space.
- Jake Archibald — The anatomy of responsive images
- WHATWG HTML Living Standard — Images
- Cloud Four — Responsive Images the Simple Way
- HTML picture element