[HTML5] Picture element, srcset for Responsive Images
keywords: img
, srcset
, size
, picture
, dpi
, dpr
, devicePixelRatio
, device pixel density
, device pixel ratio
這裡說明的主要是 <img>
標籤,而非 CSS 中的 background-image
屬性。
資料來源:Responsive Images @ MDN - Learn HTML - Multimedia and embedding
Example_Using_object_URLs_to_display_images @ Firefox Using files from web application
TL;DR
一般來說,如果你只是要變換相同圖片內容但不同解析度或不同尺寸時,使用 <img srcset>
讓瀏覽器聰明的幫你判斷:
<!-- 時機:使用到的是同樣內容但不同尺寸或不同解析度的圖片時 -->
<!-- 如果沒有需要指定圖片大小,則可以省略 size 讓瀏覽器自己判斷 -->
<img
srcset="elva-fairy-320w.jpg 320w, elva-fairy-480w.jpg 480w, elva-fairy-800w.jpg 800w"
sizes="(max-width: 320px) 280px,
(max-width: 480px) 440px,
800px"
src="elva-fairy-800w.jpg"
alt="Elva dressed as a fairy"
/>
但若你會根據瀏覽器使用到不同張內容的圖片時,那麼使用 <picture>
,寫在越後面的會被當作 fallback:
<!-- 時機:使用到不同內容的多張圖片時 -->
<picture>
<source media="(max-width: 799px)" srcset="elva-480w-close-portrait.jpg" />
<source media="(min-width: 800px)" srcset="elva-800w.jpg" />
<img src="elva-800w.jpg" alt="Chris standing up holding his daughter Elva" />
<!-- default -->
</picture>
<!-- picture 也可以拿來作為支援多種不同的 image type 使用 -->
<picture>
<source srcset="plants.avif" type="image/avif">
<source srcset="plants.webp" type="image/webp">
<img src="plants.jpg" alt="descriptive text">
</picture>
See the Pen Responsive Images with Picture element and srcset by PJCHEN (@PJCHENder) on CodePen.
碰到的問題
在面對不同裝置尺寸的圖片顯示時,常碰到的兩個問題是 art direction problem 和 resolution switching 的問題
not responsive demo @ MDN
Resolution switching problem:需要顯示不同解析度的同張圖
此外,在較小的裝置尺寸上,不需要顯示那麼尺寸很大或解析度很高的圖片,這樣的情況稱作 resolution switch problem。
Art direction problem:需要顯示不同張圖片
同樣一張圖片,在不同裝置尺寸下,因為會自動縮放的緣故,可能會碰到圖片的重點被縮得很小而看不清楚,這時可能需要根據不同的裝置尺寸切成幾張不同的圖片,讓在該裝置尺寸下仍能顯示想要被使用者看到的部分,例如當使用電腦時,顯示橫式的照片(landscape),當使用手機時則顯示直式的照片(portrait),這樣的情況稱作 art direction problem。
解決 Resolution Switching 問題
要解決 resolution switch 的問題,可以的話使用向量圖(SVG)會是最簡便的最法,因為 SVG 可以隨著裝置尺寸縮放而不會影響到解析度,但並非所以的情況都適合使用 SVG,例如照片。
Resolution switching: 顯示不同尺寸的圖片
透過在 <img>
標籤中使用 srcset
和 sizes
這兩個屬性,可以提供多個圖片資源讓瀏覽器協助挑選最適合的使用:
❗如果沒有需要指定圖片大小(例如已經使用
max-width: 100%
),則可以省略 size 讓瀏覽器自己判斷。
<img
srcset="elva-fairy-320w.jpg 320w, elva-fairy-480w.jpg 480w, elva-fairy-800w.jpg 800w"
sizes="(max-width: 320px) 280px,
(max-width: 480px) 440px,
800px"
src="elva-fairy-800w.jpg"
alt="Elva dressed as a fairy"
/>
srcset
透過 srcset
可以定義要提供哪些圖片資源讓瀏覽器使用,而瀏覽器會自動選擇最適合的:
<!--
srcset="<image-url> <descriptor>"
圖片尺寸使用 "w" 當單位,而不是 "px",填入該圖檔的實際寬度
-->
<img srcset="elva-fairy-320w.jpg 320w, elva-fairy-480w.jpg 480w, elva-fairy-800w.jpg 800w" />
<!--
也可以使用螢幕解析度 device pixel ratio 當作依據
-->
<img src="sample.jpg" srcset="sample_1x.jpg 1x, sample_2x.jpg 2x" />
在 descriptor
的部分預設是 1x
,或者可自己定義(不可並用):
width descriptor
:使用w
當單位,瀏覽器會將width descriptor
除以sizes
中所定義source width
來計算 pixel density,找出最適合的。pixel density descriptor
:使用x
當單位,直接定義特定的 device pixel ratio 時要使用哪種圖檔。
attr-srcset @ MDN > Web technology for developers
sizes
sizes
定義一系列的媒體條件(例如螢幕寬度)和搭配的圖片顯示寬度。需搭配 srcset
使用,會根據這裡所設定的 source size
來決定 srcset
要取用哪一張圖片;但是當 srcset
屬性不存在,或是並非使用 w
作為單位時,sizes
不會有任何效果:
<!--
sizes="<media condition> <source size>
media condition (max-width: 480px)
source size: 當媒體條件成立時該圖片的寬度
-->
<img
sizes="(max-width: 320px) 280px,
(max-width: 480px) 440px,
800px"
/>
source size
會是在該 media condition 成立時,該圖片顯示的寬度(沒有使用其他 css 時才會套用到),對於 source size
可以使用絕對長度(px
, em
)或者使用相對長度(vw
),但不能用百分比。最後一個 slot 不會定義媒體條件,以作為預設尺寸。一旦瀏覽器找到配對到的媒體條件時,就會忽略其他的條件,所以要留意放置的順序。
- attr-size @ MDN > Web technology for developers
- srcset size demo @ MDN
Resolution switching: 相同尺寸不同解析度的圖 片
如果你需要支援多種不同解析度的顯示器,但使用的是相同尺寸的圖片,你可以在 srcset
中使用 x
來表示解析度,不需要定義 size
屬性,瀏覽器會自動挑選:
<!--
srcset="<image-url> <resolution>
-->
<img
srcset="elva-fairy-320w.jpg, elva-fairy-480w.jpg 1.5x, elva-fairy-640w.jpg 2x"
src="elva-fairy-640w.jpg"
alt="Elva dressed as a fairy"
/>
srcset resolution demo @ MDN
瀏覽器運作過程
- 先看裝置寬度
- 找出在
sizes
中所定義的 media condition 有沒有符合的 - 找出在
sizes
中 media condition 符合時所給的 slot size - 根據和 slot size 最相近的大小,載入
srcset
中的圖片
解決 Art Direction 問題
在 art direction problem 中,我們希望根據不同的顯示器裝置尺寸顯示不同的圖片。例如,當在電腦上面放置一張中間有人物的橫式(landscape)的照片,當隨著尺寸越來越小時,這個人物也會變得很小,因此,比較好的作法是在較小的螢幕裝置下,顯示直式(portrait)的人像照片。
透過 <picture>
元素可以達到這樣的效果,<picture>
元素就像 <video>
, <audi>
元素一樣,可以裡面放置許多不同的 sources 讓瀏覽器從中選擇,我們可以把原本的 <img>
標籤改成:
<picture>
<source media="(max-width: 799px)" srcset="elva-480w-close-portrait.jpg" />
<source media="(min-width: 800px)" srcset="elva-800w.jpg" />
<img src="elva-800w.jpg" alt="Chris standing up holding his daughter Elva" />
<!-- default -->
</picture>
- 在
<source>
中使用media
屬性可以定義 media condition,第一個符合 media condition 的<source>
會被用來顯示。 - 在
<source>
中的srcset
屬性則包含要顯示的圖片路徑。另外一樣也可以使用sizes
屬性。 - 在
<picture>
的最後,你需要使用<img src="" alt="">
,這是作為沒有任何 media condition 成立時使用的預設條件。
❗ 只有要解決 art direction 的情況時才使用
media
屬性,當使用了media
屬性就,就不能再使用sizes
屬性。
下面這兩張圖可以看出有使用 <picture>
和沒有使用的差別:
左圖沒有解決 art direction 的問題,因此圖片會隨瀏覽器尺寸自然縮小;右圖有使用
<picture>
解決 art direction 的問題,因此當裝置尺寸變小時,會使用直式(portrait)的圖片。 picture element demo @ MDN
透過 <picture>
搭配 type
屬性,也可以解決當瀏覽器不支援特定格式的圖片時作為 fallback 使用:
⚠️ 注意:
source
的順序是重要的,寫在越後面會當成 fallback。
<!--
當瀏覽器不支援 webp 或 jp2 檔案格式時,
fallback 回 jpg
-->
<picture>
<source srcset="sample.webp" type="image/webp" />
<source srcset="sample.jp2" type="image/jp2" />
<img src="sample.jpg" alt="Sample" />
</picture>
同時使用 picture 和 img srcset
<picture>
<source
type="image/webp"
srcset="
media/images/b2b_index/bg_index_1.webp 1x,
media/images/b2b_index/bg_index_1@2x.webp 2x,
media/images/b2b_index/bg_index_1@3x.webp 3x
"
/>
<source
type="image/png"
srcset="
media/images/b2b_index/bg_index_1.png 1x,
media/images/b2b_index/bg_index_1@2x.png 2x,
media/images/b2b_index/bg_index_1@3x.png 3x
"
/>
<img src="'media/images/b2b_index/bg_index_1.png'" />
</picture>
Mixing srcset and picture for responsive images @ StackOverflow
常見問題
為什麼要透過 HTML 而不直接透過 CSS 或 JavaScript 來解決
透過 CSS 或 JavaScript 時,沒辦法第一時間就根據螢幕裝置的尺寸去請求下載適合的圖片,而是必須要一次下載兩種以上尺寸的圖片後,才能透過 JS 把圖片換掉。
支援使用不同的圖片格式
WebP
是一種可以維持小檔案和高品質圖片的檔案格式,但瀏覽器的支援度仍不高,此時可以透過 <picture>
,搭配 type
屬性定義 MIME Type,當該檔案格式不支援時,瀏覽器會直接回絕它:
<picture>
<source type="image/svg+xml" srcset="pyramid.svg" />
<source type="image/webp" srcset="pyramid.webp" />
<img src="pyramid.png" alt="regular pyramid built from four equilateral triangles" />
</picture>
- 不要使用
media
屬性,除非你同持需要解決 art direction 問題 - 在
<source>
元素中,你只能載入符合該type
格式的圖片 - 有需要的話,一樣可以使用
srcset
和sizes
屬性
該使用 <img srcset>
還是 <picture>
一般來說,如果你只是要變換相同圖片但不同解析度或不同尺寸時,使用 <img srcset>
讓瀏覽 器聰明的幫你判斷;但若你會根據瀏覽器使用到不同張圖片時,那麼使用 <picture>
。
使用 <img srcset="">
時,瀏覽器會根據使用者的螢幕解析度和其他情境來挑選最適合的圖,且瀏覽器會以第一個時間點判斷到的裝置尺寸去請求圖片;如果後來把瀏覽器縮小,仍然會使用先前快取好(尺寸較大)的那張;但若是把瀏覽器尺寸變大,則會再請求解析度更好的圖檔,但若再次縮小瀏覽器的話,仍然會使用先前快取好(尺寸較大)的那張。
使用 <picture>
時則會完全根據當時的 viewport 去載入圖片,因此當裝置尺寸改變的時候,不需要重新載入瀏覽器就能看到。
❗要留意的是如果你使用的是 retina 或 4k 裝置,使用
<img srcset="">
時,瀏覽器會因為解析度的關係,會去選擇更高尺寸的圖片。 Responsive Images: If you’re just changing resolutions, use srcset @ CSS Tricks
如果要套用在 CSS 上
如果要把 srcset
或 size
類似的用法套用到在 CSS 載入圖片時,可以參考這篇:Responsive Images CSS @ CSS Tricks
檢視裝置 pixel density
window.devicePixelRatio;
資料來源
- :thumbsup: Responsive Images @ Summer:說明許多不同情況下可以使用的方式
- Responsive Images @ MDN - Learn HTML - Multimedia and embedding
- Responsive Images CSS @ CSS Tricks