網頁在不同尺寸的裝置上,都有良好的顯示效果,叫做"響應式設計"(responsive web design)。
響應式設計的網頁影像,就是"響應式影像"(responsive image)。
響應式影像的解決方案有很多,JavaScript 和 CSS 都可以實現。本文介紹最簡單的、語義性最好的 HTML 方法,瀏覽器原生支援。
一、問題的由來
我們知道,<img>
標籤用於插入網頁影像,所有情況預設插入的都是同一張影像。
<img src="foo.jpg">
上面程式碼在桌面端和手機上,插入的都是影像檔案foo.jpg
。
這種處理方法固然簡單,但是有三大弊端。
(1)體積
一般來說,桌面端顯示的是大尺寸的影像,檔案體積較大。手機的螢幕較小,只需要小尺寸的影像,可以節省頻寬,加速網頁渲染。
(2)畫素密度
桌面顯示器一般是單倍畫素密度,而手機的螢幕往往是多倍畫素密度,即多個畫素合成為一個畫素,稱為 Retina 螢幕。影像檔案很可能在桌面端很清晰,放到手機上會有點模糊,因為畫素擴充了。
(3)視覺風格
桌面顯示器的面積較大,影像可以容納更多細節。手機的螢幕較小,許多細節是看不清的,需要突出重點。
上面兩張圖片,下方的手機圖片經過裁剪以後,更突出影像重點,明顯效果更好。
二、畫素密度的選擇:srcset
屬性
為了解決上一節的這些問題,HTML 語言提供了一套完整的解決方案。首先,<img>
標籤引入了srcset
屬性。
srcset
屬性用來指定多張影像,適應不同畫素密度的螢幕。它的值是一個逗號分隔的字串,每個部分都是一張影像的 URL,後面接一個空格,然後是畫素密度的描述符。請看下面的例子。
<img srcset="foo-320w.jpg, foo-480w.jpg 1.5x, foo-640w.jpg 2x" src="foo-640w.jpg">
上面程式碼中,srcset
屬性給出了三個影像 URL,適應三種不同的畫素密度。
影像 URL 後面的畫素密度描述符,格式是畫素密度倍數 + 字母x
。1x
表示單倍畫素密度,可以省略。瀏覽器根據當前裝置的畫素密度,選擇需要載入的影像。
如果srcset
屬性都不滿足條件,那麼就載入src
屬性指定的預設影像。
三、影像大小的選擇:srcset
屬性 + sizes
屬性
畫素密度的適配,只適合顯示區域一樣大小的影像。如果希望不同尺寸的螢幕,顯示不同大小的影像,srcset
屬性就不夠用了,必須搭配sizes
屬性。
第一步,srcset
屬性列出所有可用的影像。
<img srcset="foo-160.jpg 160w, foo-320.jpg 320w, foo-640.jpg 640w, foo-1280.jpg 1280w" src="foo-1280.jpg">
上面程式碼中,srcset
屬性列出四張可用的影像,每張影像的 URL 後面是一個空格,再加上寬度描述符。
寬度描述符就是影像原始的寬度,加上字元w
。上例的四種圖片的原始寬度分別為160畫素、320畫素、640畫素和1280畫素。
第二步,sizes
屬性列出不同裝置的影像顯示寬度。
sizes
屬性的值是一個逗號分隔的字串,除了最後一部分,前面每個部分都是一個放在括號裡面的媒體查詢表示式,後面是一個空格,再加上影像的顯示寬度。
<img srcset="foo-160.jpg 160w, foo-320.jpg 320w, foo-640.jpg 640w, foo-1280.jpg 1280w" sizes="(max-width: 440px) 100vw, (max-width: 900px) 33vw, 254px" src="foo-1280.jpg">
上面程式碼中,sizes
屬性給出了三種螢幕條件,以及對應的影像顯示寬度。寬度不超過440畫素的裝置,影像顯示寬度為100%;寬度441畫素到900畫素的裝置,影像顯示寬度為33%;寬度900畫素以上的裝置,影像顯示寬度為254px
。
第三步,瀏覽器根據當前裝置的寬度,從sizes
屬性獲得影像的顯示寬度,然後從srcset
屬性找出最接近該寬度的影像,進行載入。
假定當前裝置的螢幕寬度是480px
,瀏覽器從sizes
屬性查詢得到,圖片的顯示寬度是33vw
(即33%),等於160px
。srcset
屬性裡面,正好有寬度等於160px
的圖片,於是載入foo-160.jpg
。
注意,sizes
屬性必須與srcset
屬性搭配使用。單獨使用sizes
屬性是無效的。
四、<picture>
標籤,<source>
標籤
上面兩節分別解決了畫素密度和螢幕大小的適配,但是如果要同時適配不同畫素密度、不同大小的螢幕,應該怎麼辦呢?
這時,就要用到<picture>
標籤。它是一個容器標籤,內部使用<source>
和<img>
,指定不同情況下載入的影像。
<picture> <source media="(max-width: 500px)" srcset="cat-vertical.jpg"> <source media="(min-width: 501px)" srcset="cat-horizontal.jpg"> <img src="cat.jpg" alt="cat"> </picture>
上面程式碼中,<picture>
標籤內部有兩個<source>
標籤和一個<img>
標籤。
<source>
標籤的media
屬性給出媒體查詢表示式,srcset
屬性就是<img>
標籤的srcset
屬性,給出載入的影像檔案。sizes
屬性其實這裡也可以用,但由於有了media
屬性,就沒有必要了。
瀏覽器按照<source>
標籤出現的順序,依次判斷當前裝置是否滿足media
屬性的媒體查詢表示式,如果滿足就載入srcset
屬性指定的圖片檔案,並且不再執行後面的<source>
標籤和<img>
標籤。
<img>
標籤是預設情況下載入的影像,用來滿足上面所有<source>
都不匹配的情況。
上面例子中,裝置寬度如果不超過500px
,就載入豎屏的影像,否則載入橫屏的影像。
下面給出一個例子,同時考慮螢幕尺寸和畫素密度的適配。
<picture> <source srcset="[email protected], [email protected] 2x" media="(min-width: 990px)"> <source srcset="[email protected], [email protected] 2x" media="(min-width: 750px)"> <img srcset="[email protected], [email protected] 2x" alt="Shopify Merchant, Corrine Anestopoulos"> </picture>
上面程式碼中,<source>
標籤的media
屬性給出螢幕尺寸的適配條件,每個條件都用srcset
屬性,再給出兩種畫素密度的影像 URL。
五、<source>
標籤的type
屬性
除了響應式影像,<picture>
標籤還可以用來選擇不同格式的影像。比如,如果當前瀏覽器支援 Webp 格式,就載入這種格式的影像,否則載入 PNG 影像。
<picture> <source type="image/svg+xml" srcset="logo.xml"> <source type="image/webp" srcset="logo.webp"> <img src="logo.png" alt="ACME Corp"> </picture>
上面程式碼中,<source>
標籤的type
屬性給出影像的 MIME 型別,srcset
是對應的影像 URL。
瀏覽器按照<source>
標籤出現的順序,依次檢查是否支援type
屬性指定的影像格式,如果支援就載入影像,並且不再檢查後面的<source>
標籤了。上面例子中,影像載入優先順序依次為 svg 格式、webp 格式和 png 格式。
六、參考連結
- Responsive Images 101, Jason Grigsby
- Responsive images, MDN
(完)