響應式圖片(Responsive Images)

在行動裝置上關於圖片常遇到圖檔體積過大或解析度不佳等問題,這些問題可經由選擇適當的圖片格式、壓縮、設定適合的尺寸、依照解析度提供適當的圖片來解決,本文要探討的是如何在 HTML 上使用 <picture>/<img srcset sizes> 來讓瀏覽器根據自身環境選擇適當的圖檔。

響應式網頁可根據自身環境選擇適當的圖檔,選擇的依據主要是下面兩個條件

狀況一:我在安卓上都很正常,但為什麼在 iPhone 上,圖片都會糊糊的?

這是因為一般的螢幕 DPR 大多是 1,而蘋果裝置像是手機是 2,平板電腦可能是 3。若在 Retina 這種高解析度(Hight DPI)螢幕上,仍使用提供給低解析度螢幕的圖片,就會看起來模糊不清。

解法:在 srcset 設定 Pixel Density Descriptor,並分別給予不同的圖片來源。

如下,在 DPR 為 1 的狀況下,瀏覽器會選擇 sample_1x.jpg 這張圖;而在 DPR 為 2 的狀況下,瀏覽器會選擇 sample_2x.jpg 這張圖;若瀏覽器不支援 srcset 或選不到適合的檔案,就會讀取 sample.jpg。

<img src="sample.jpg" srcset="sample_1x.jpg 1x, sample_2x.jpg 2x" />

可利用 console 查看設備的 Pixel Ratio。

window.devicePixelRatio;

備註:(2021/07/31 更新) Chrome 從版本 73 開始,<img> 可經由設定 srcset 屬性來 preload 適當尺寸的響應式圖片,而不是等到 HTML parser 解析到 <img> 標籤的時候才載入該圖檔。而 <picture> 目前是沒有 preload 的機制的,需使用 <link rel="preload" /> 來做搭配。

狀況二:我不確定 DPR,可以讓瀏覽器自己決定它需要什麼尺寸的檔案嗎?

答案是「可以」,我們可以只設定畫出這張圖確實需要多少 pixel,只要總和對了就選這張圖。

解法:使用「w」,w 表示瀏覽器會依照 DPR 和 Viewport 決定圖片的寬度。

如下,假設螢幕的寬度是 500px 且 DPR 是 2,則會選擇圖片的尺寸是 500px * 2 = 1000px,也就是 medium.jpg;而一般 DPI 為 1 的螢幕,就會選到 small.jpg 這張圖;若瀏覽器不支援 srcset 或選不到適合的檔案,就會讀取 src 所設定的 small.jpg。

<img src="small.jpg" srcset="small.jpg 500w, medium.jpg 1000w, large.jpg 1500w" />

注意,使用 w 就必須要為圖片指定大小。這是因為在讀取 CSS 前,瀏覽器會先解析 HTML 並開始載入圖片(Preload),而此時瀏覽器並不知道圖片的大小,因此需要設定 sizes 屬性來告知到底要選哪一種尺寸的圖片來載入。注意,sizes 屬性並不會調整圖片尺寸,只是告知瀏覽器選擇圖片尺寸的條件,不設定就會當成跟 Viewport 一樣寬(等於 100vw),所以若要調整圖片大小仍需使用 CSS 來控制。

如下,若 Viewport 小於或等於 500px,則圖片的寬度為 Viewport 的 90%,其餘狀況都是 Viewport 的 60%;若螢幕寬度是 500px 且 DPR 是 1,則所需圖片尺寸是 500 _ 0.9 _ 1 = 450(px),就會選到 small.jpg。

<img
  src="small.jpg"
  sizes="(max-width: 500px) 90vw, 60vw"
  srcset="small.jpg 500w, medium.jpg 1000w, large.jpg 1500w"
/>

狀況三:如何根據 Viewport 指定不同大小的圖檔?

將 Media Query 的條件寫在 media 屬性即可依照 viewport 選擇不同圖片來源。

如下,在 Viewport 大於或等於 650px 的狀況下,瀏覽器會選擇 sample-large.jpg 這張圖;Viewport 在大於或等於 465px 且小於 650px 的狀況下,瀏覽器會選擇 sample-medium.jpg 這張圖;若都不符合以上條件或瀏覽器不支援 srcset,就會選到 sample-small.jpg 這張圖。

<picture>
  <source media="(min-width: 650px)" srcset="sample-large.jpg" />
  <source media="(min-width: 465px)" srcset="sample-medium.jpg" />
  <img src="sample-small.jpg" alt="Sample" />
</picture>

狀況四:如何同時顧及 DPR 與 Viewport?

由於在不同設備上的排版並不相同,因此希望能讀取不同大小的圖檔以節省網路資源;並且也希望能在高解析度螢幕上有好的呈現。那麼,我可以在 media 屬性上設定 Media Query 條件,並在 srcset 設定根據不同 DPR 選擇不同圖片來源。

如下,在 Viewport 為 700px 的狀況下,若設備的 DPI 為 2,則瀏覽器會選到 sample-large-2x.jpg 這張圖。

<picture>
  <source media="(min-width: 600px)" srcset="sample-large-1x.jpg 1x, sample-large-2x.jpg 2x" />
  <source media="(min-width: 400px)" srcset="sample-medium-1.jpg 1x, sample-medium-2.jpg 2x" />
  <img src="sample-small.jpg" alt="Sample" />
</picture>

狀況五:如何加上個別瀏覽器所支援的新型圖檔格式?

嘗試使用新的圖片格式,但要考慮不支援的瀏覽器的 fallback 策略。

如下,若瀏覽器支援第一個檔案格式 WebP,就用第一個;否則依序詢問接下來設定的圖片資源,像是詢問第二個檔案格式 JPEG 2000。fallback 為 <img> src 屬性所設定的圖片。

<picture>
  <source srcset="sample.webp" type="image/webp" />
  <source srcset="sample.jp2" type="image/jp2" />
  <img src="sample.jpg" alt="Sample" />
</picture>

瀏覽器支援度

除了 IE 和 Opera Mini 外,其他主流瀏覽器都支援。

srtset 瀏覽器支援度

請見-Srcset and sizes attributes

後記

這裡提到的 <picture>/<img srcset sizes> 和過去在 CSS 中使用 Media Query 有什麼不同呢?這裡所提到的方法將選擇權交給瀏覽器,讓瀏覽器在解析 HTML 時依當時狀況決定要下載什麼樣的圖片,而 Media Query 並沒有讓瀏覽器選擇,只是全部都下載下來(浪費網路頻寬,導致畫面延遲顯示),再依照執行狀況決定要用什麼圖檔,是不一樣的。

References


圖片最佳化 效能調校 加載效能 Image Optimization Loading Performance 響應式網頁 Responsive Web Design Media Query Core Web Vitals Web Vitals 前端效能 系列文