iT邦幫忙

2025 iThome 鐵人賽

DAY 15
0
Modern Web

前端迷走中:從零開始的新手之路系列 第 15

[Day 15] 硬是響應式──sizes 與 <picture> 讓圖片跟上螢幕尺寸

  • 分享至 

  • xImage
  •  

昨天我們解釋了什麼是像素密度,也介紹怎麼透過<img>元素的srcset屬性,為同一張圖片提供其他像素密度版本的檔案。

不過除了像素密度,我們還需要考量瀏覽裝置的尺寸。

由於圖片在不同裝置上可能會有不一樣的顯示尺寸。如果只有一種版本可以使用,可能圖片尺寸(總像素)對大螢幕裝置來說太小,或對小螢幕裝置來說太小。

雖然可以使用昨天提到的像素密度描述符提供多種版本。但是用像素密度來區分圖片尺寸,不直覺也很複雜。

假如需要對多種顯示尺寸提供多種像素密度的圖片版本,也可能會有不同版本的總像素其實一樣,只是在不同顯示尺寸下,像素密度才不同的問題。

這時候我們可以改用srcset屬性的寬度描述符,將圖片依尺寸區分為不同版本。

結合srcsetsizes屬性,為同張圖片提供不同尺寸的版本

srcset的寬度描述符需搭配size屬性使用

不過如果單純用srcset屬性的寬度描述符區分圖片版本,瀏覽器可能也不確定要使用哪個尺寸的圖片。

因為瀏覽器在下載圖片時,可能還沒處理好CSS設定。瀏覽器只知道瀏覽裝置目前的viewport,不確定網頁實際的版面佈局,以及圖片在這個版面佈局中要顯示的尺寸。[1]

所以使用srcset屬性的寬度描述符時,需要搭配另一個sizes屬性。這樣瀏覽器才能依據顯示尺寸,判斷哪個版本提供的圖片尺寸(總像素)最適合使用。

size屬性簡介

透過sizes屬性,能夠設定圖片在不同媒體條件下的顯示尺寸。

這裡的媒體條件包括剛剛提到的viewport。關於viewport,我們在第四天有介紹過,指的是網頁在裝置上的顯示範圍,通常會以寬度來描述。

設定sizes的屬性值時,每一種媒體條件與其對應的圖片顯示尺寸會以一組字串表示。媒體條件用括弧包起來,放在字串的開頭。圖片顯示尺寸放在結尾,以空格與前面的媒體條件相隔。

如果同時設定多個條件與尺寸,會以逗號相隔每組字串。最後一組字串表示所有媒體條件都不符合時,圖片會顯示的尺寸,所以不用寫出媒體條件。

圖片顯示尺寸以寬度來設定,規則如下:

  • 寬度需要是非負數。
  • 單位可以是相對長度或絕對長度,但不能是百分比。
  • 如果是相對長度,則相對於整個網頁的viewport,而非<img>元素本身。
  • 除了指定一個特定值,也可以用CSS的math function來計算。

如同前面說的,sizes屬性與srcset屬性的寬度描述符需要一起使用,兩者缺一不可。

瀏覽器怎麼判斷

實際運作上,瀏覽器會先根據sizes屬性的設定,判斷裝置目前的viewport符合哪一個媒體條件,因此圖片要用哪一個尺寸(寬度)來顯示。

接著,瀏覽器會比對srcset的屬性值,將每一個圖片版本的寬度除以圖片顯示尺寸的寬度,得出像素密度。[2]

之後,瀏覽器會再根據其他條件,像是裝置的像素密度、瀏覽器的縮放等級,或是使用者的網路設定,判斷哪一個圖片版本最適合使用。所以最終顯示的圖片,不見得會是大小最接近顯示尺寸的那個版本。

需要注意的是,瀏覽器在判斷媒體條件時會依照設定的順序。如果目前的裝置符合前面的媒體條件,瀏覽器就會不會往下判斷有沒有其他更貼近的媒體條件。所以設定sizes屬性時,應該以適當的順序排列這些字串。

範例

以下由MDN提供的例子中,透過sizes屬性設定了兩組媒體條件與圖片顯示尺寸(寬度)。同時搭配srcset屬性,提供兩種寬度不同的圖片版本。

因為只有設定兩組條件,所以判斷圖片顯示尺寸時會以第一組的媒體條件(width <= 600px)作為分水嶺。

如果裝置目前的viewport寬度小於等於600px,則圖片的顯示尺寸會是480px;如果大於600px,則圖片的顯示尺寸會是800px。

得到圖片顯示尺寸後,瀏覽器會依據srcset屬性,計算出每個版本以這個尺寸顯示下的像素密度。之後再結合其他條件,判斷最終要使用哪一個版本的圖片。

即便瀏覽器不支援srcset屬性,也因為有設定src屬性,可以使用寬度為800px的那個版本。

<img
  srcset="elva-fairy-480w.jpg 480w, elva-fairy-800w.jpg 800w"
  sizes="(width <= 600px) 480px,
         800px"
  src="elva-fairy-800w.jpg"
  alt="Elva dressed as a fairy" />

<picture>元素,依裝置尺寸提供不同內容的圖片

對於不同尺寸的裝置,除了提供同一張圖片的多個版本,有時候也需要提供不同內容的圖片。

可能圖片是以遠景拍攝,所以裡頭的人物在手機等小螢幕的裝置上看起來會太小,需要再手動放大才看得清楚。

這時候我們可以使用<picture>元素,提供多組內容不同的圖片。讓瀏覽器針對小螢幕的裝置,載入裁切後的圖片。

<picture>元素簡介

<picture><img>不同,不是空元素。所以在<picture>元素裡,可以加入多個<source>元素,同時提供不同圖片的路徑。

不過使用<picture>時,仍然必須包括<img>,不然不會顯示任何圖片。[3]如此也可以避免有些瀏覽器不支援<picture>

通常會將<img>作為其他<source>都不適用時的最終選項。使用時同樣需要加上alt屬性,提供圖片的替代文字。

<source>元素簡介

<picture>不同,<source>是空元素。

放在<picture>裡頭使用時,<source>只能使用srcset屬性提供圖片的檔案路徑;不過如果是在<video><audio>元素裡,<source>反而只能使用src提供路徑。[4]

使用<source>元素時,通常會以media屬性設定<source>本身適用的條件。media類似於sizes屬性,但沒有指定圖片的顯示尺寸。

srcset屬性設定檔案路徑時,也可以使用像素密度描述符或寬度描述符,提供不同像素密度或寬度的圖片版本給瀏覽器挑選。

同樣的,如果srcset使用了寬度描述符,就需要另外搭配sizes屬性,兩者缺一不可。[4]

範例

以下由MDN提供的範例,透過<picture>提供兩張不同的圖片。

如果瀏覽裝置目前的viewport寬度大於等於800px,會使用第一個<source>提供的大張圖片;如果小於800px,則使用第二個<source>提供的小張圖片。

如果瀏覽器不支援<picture>,則會使用<img>提供的大張圖片。

<picture>
  <source media="(width < 800px)" srcset="elva-480w-close-portrait.jpg" />
  <source media="(width >= 800px)" srcset="elva-800w.jpg" />
  <img src="elva-800w.jpg" alt="Chris standing up holding his daughter Elva" />
</picture>

在另一個由規範提供的例子中,同樣提供兩張不同的圖片,不過srcset的屬性值同時又使用了像素密度描述符。

如果瀏覽裝置目前的viewport寬度小於等於500px,符合media設定的條件,則會使用<source>提供的小張圖片;
如果viewport寬度大於500px,或是瀏覽器不支援<picture>元素,則會使用<img>提供的大張圖片。

由於使用了像素密度描述符,瀏覽器在決定用哪一張圖片後,還會再依據瀏覽裝置的像素密度,以及其他條件,判斷使用哪一個像素密度版本的圖片比較合適。

<h1>
 <picture>
  <source media="(max-width: 500px)" srcset="banner-phone.jpeg, banner-phone-HD.jpeg 2x">
  <img src="banner.jpeg" srcset="banner-HD.jpeg 2x" alt="The Breakfast Combo">
 </picture>
</h1>

小結

今天我們介紹了怎麼依據裝置尺寸,提供不同內容的圖片,或是同張圖片不同尺寸的版本。

如果要為同一張圖片提供不同尺寸的版本,需要同時使用sizes屬性以及srcset屬性的寬度描述符。由前者判斷圖片的顯示尺寸,由後者區分圖片不同尺寸的版本。

針對小螢幕裝置提供尺寸較小的版本,可以提高圖片載入速度,也節省網路用量。

如果是想提供不同內容的圖片,則需使用<picture>,與其中的<source><img>元素來達成。

假如對小螢幕裝置另外提供裁切後的圖片,就可以避免人物變得太小而看不清楚的問題。

參考資料

[1]: Jason Grigsby, Responsive Images 101, Part 4: Srcset Width Descriptors
[2]: HTML Standard, Viewport-based selection
[3]: MDN, Using responsive images in HTML
[4]: MDN, <source>: The Media or Image Source element


上一篇
[Day 14] 硬是響應式──srcset 讓圖片看得更清楚
下一篇
[Day 16] 暫別 HTML──引文、程式碼與通用容器
系列文
前端迷走中:從零開始的新手之路21
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言