描述性語法

在本單元中,您將瞭解如何為瀏覽器選擇圖片,讓瀏覽器做出最適當的顯示決定。srcset 不是在特定中斷點交換圖片來源的方法,也不是用來將圖片更換為其他圖片。這些語法可讓瀏覽器解決非常困難的問題,與我們無關:順暢地要求並轉譯專為使用者的瀏覽情境量身打造的圖片來源,包括可視區域大小、顯示密度、使用者偏好設定、頻寬和其他無數因素。

這只是個大問題,我們不只是想考慮在網路中標記圖片時,還牽涉到更多的資訊,而難以存取。

使用 x 說明密度

無論使用者螢幕的密度為何,也就是顯示畫面的實體像素數量,固定寬度的 <img> 在所有瀏覽環境中都會佔據的可視區域大小。舉例來說,原始寬度為 400px 的圖片會佔用在舊版 Google Pixel 和較新款 Pixel 6 Pro 上的整個瀏覽器可視區域,因為這兩款裝置都具有正規化的 412px 邏輯像素寬可視區域。

Pixel 6 Pro 的螢幕較清晰,不過 Pixel 6 Pro 的實體解析度為 1440 × 3120 像素,Pixel 則為 1080 × 1920 像素,也就是構成螢幕本身的硬體像素數量。

裝置邏輯像素與實體像素之間的比率,是指螢幕 (DPR) 的裝置像素比例。DPR 的計算方式是將裝置的實際螢幕解析度除以可視區域的 CSS 像素。

主控台視窗中顯示的 DPR 為 2。

因此,第一代 Pixel 的 DPR 為 2.6,Pixel 6 Pro 的 DPR 為 3.5。

iPhone 4 是第一部 DPR 大於 1 的裝置,裝置像素比例為 2,螢幕的實際解析度為邏輯解析度的兩倍。凡是搭載 iPhone 4 之前的裝置,DPR 的 DPR 為 1:從一個邏輯像素到一個實體像素。

如果你在螢幕上顯示 400px 寬幅圖片,且 DPR 為 2,則每個邏輯像素都會在螢幕的四個實體像素中算繪:兩個水平和兩個垂直像素。這張圖片並未受益於高密度螢幕,其外觀與顯示 1 的螢幕顯示效果相同。當然,瀏覽器的算繪引擎 (例如文字、CSS 形狀或 SVG) 所「繪製」的內容,都會按照較高密度的顯示比例調整。不過,如同圖片格式和壓縮所述,光柵圖片是固定的像素。儘管這張光柵圖片未必顯而易見,但為了配合較高密度的螢幕,將光柵圖片放大,其解析度看起來就會比周圍頁面低。

為了避免放大,系統算繪的圖片本身寬度必須至少為 800 像素。當螢幕縮小到版面配置為 400 邏輯像素寬度時,800 像素的圖片來源就會有兩倍像素密度 (在 DPR 為 2 的螢幕上) 會呈現出精美清晰的效果。

花瓣的特寫圖,呈現密度不同的程度。

由於 DPR 為 1 的螢幕無法利用增加的圖片密度,因此會「縮減」以配合螢幕。如您所知,「縮小」的圖片看起來很正常。在低密度螢幕上,適合高密度螢幕的圖片看起來會與其他低密度圖片一樣。

如「圖片和效能」一文所述,如果使用者查看圖片來源已縮小為 400px 的低密度螢幕,就「需要」本身寬度為 400px 的來源。儘管較大的圖片對所有使用者來說都適用,但是在低密度螢幕中轉譯大型圖片來源時,看起來就像任何其他小型低密度圖片,但「較為慢」

您或許已經猜到,DPR 為 1 的行動裝置很少見,但仍然常見於「電腦」的瀏覽環境。根據 Matt Hobbs 提供的資訊,自 2022 年 11 月起,在 GOV.UK 瀏覽工作階段中,約有 18% 的 DPR 為 1。雖然高密度圖片的「看起來」符合使用者預期的樣子,但其頻寬和處理成本也更高。對舊款和效能較低的裝置,可能還是會使用低密度螢幕的使用者尤其重要。

使用 srcset 可確保只有高解析度螢幕裝置接收到夠大的圖片來源,而不會將相同的頻寬費用提供給低解析度螢幕的使用者。

srcset 屬性可識別一或多個以半形逗號分隔的候選項目,用於算繪圖片。每個候選項目都由兩個項目組成:網址 (就像您在 src 中使用的網址),以及圖片來源的語法說明srcset每個候選文字都能透過其本身的寬度 (「w語法」) 或適用的密度 ("x語法) 描述

x 語法是「此來源適合具有此密度的螢幕」的簡寫,在後方加上 2x 的候選文字,適用於 DPR 為 2 的螢幕。

<img src="low-density.jpg" srcset="double-density.jpg 2x" alt="...">

支援 srcset 的瀏覽器會顯示兩個候選項目:double-density.jpg2x 描述適合以 DPR 為 2 的顯示,在 src 屬性中則適合使用 low-density.jpg;如果 srcset 中沒有更合適的選項,則會選擇候選。如果瀏覽器不支援 srcset,系統會忽略屬性及其內容,並照常要求 src 的內容。

srcset 屬性指定的值很容易出錯。2x 會告知瀏覽器關聯來源檔案適合在 DPR 為 2 的螢幕上使用,也就是來源本身的相關資訊。但不會指示瀏覽器如何使用該來源,只會通知瀏覽器如何使用來源。這個區別十分細微但重要:這是雙重密度的「圖片」,而不是適用於雙密度「螢幕」的圖片。

與「此來源適合 2x 螢幕」的語法相比,與「在 2x 螢幕上使用這個來源」的語法有些差異,但顯示密度只是瀏覽器用來決定要算繪的大量交互關係因素之一,但其中只有你能知道的。舉例來說,您可以分別判斷使用者是否已透過 prefers-reduced-data 媒體查詢判斷使用者是否已啟用節省頻寬的瀏覽器偏好設定,並據此選擇一律為使用者啟用低密度圖片,無論使用者的顯示密度為何。不過,除非每位開發人員在每個網站上都以一致的方式實作,否則不太適合使用者使用。因為他們可能在某個網站上受到其偏好,並在下一次後在圖片牆上陷入寬頻。

srcset/sizes 使用的某些資源選擇演算法十分模糊,會讓瀏覽器決定選擇較低密度的圖片 (頻寬不足,或是根據偏好盡量降低資料用量),而不必費心決定方式、時間或達到門檻。你要負起責任和其他工作,那時 Chrome 瀏覽器依然具備最強大的功能。

使用 w 說明寬度

srcset 接受圖片來源候選的第二種描述元。這個模式更強大,用途也更易理解。w 語法會說明每個候選來源的固有寬度,而不是將候選項目標記為具有特定顯示密度的適當尺寸。同樣地,每個候選項目的尺寸都相同,也就是內容相同、裁剪相同,而且顯示比例相同。但以目前來說,您會希望使用者的瀏覽器選擇兩個候選文字:small.jpg (本身寬度為 600px) 和 large.jpg (原始寬度為 1200px 的來源)

srcset="small.jpg 600w, large.jpg 1200w"

這項操作不會指示瀏覽器如何「處理」這些資訊,只要提供圖片的候選清單即可。 您需要先提供額外資訊,比如圖片在網頁上的轉譯方式,瀏覽器才能決定要轉譯哪一個來源。方法是使用 sizes 屬性。

透過 sizes 說明用量

瀏覽器在轉移圖片方面效能十分出色。系統會在對圖片素材資源發出要求之前,先發出很長一段時間的請求,然後才提出樣式表或 JavaScript 的要求,通常是在標記完成剖析前。瀏覽器發出這些要求時,除了標記以外,瀏覽器本身沒有任何網頁相關資訊,甚至可能還沒有對外部樣式表發出要求,更別說再做。瀏覽器剖析標記並開始提出外部要求時,只會具備瀏覽器層級資訊,包括使用者的可視區域大小、使用者的螢幕像素密度、使用者偏好設定等。

這不會說明圖片在網頁版面配置中的轉譯方式,甚至也無法使用可視區域做為 img 大小上限的 Proxy,因為它可能會佔用水平捲動容器。因此,我們需要向瀏覽器提供這些資訊,並使用標記來達成。那我們就可以針對這些要求使用上述資訊。

srcset 一樣,sizes 的作用是在標記剖析後立即提供圖片相關資訊。就像「這裡是來源檔案及其固有大小」的 srcset 屬性一樣,sizes 屬性是「此處為版面配置中算繪圖片的大小」的簡寫。描述圖片的方式取決於可視區域,同樣地,可視區域大小是瀏覽器發出圖片要求時,瀏覽器唯一具備的版面配置資訊。

這聽起來可能有些偏誤,但在實務上會比較容易理解:

<img
 sizes="80vw"
 srcset="small.jpg 600w, medium.jpg 1200w, large.jpg 2000w"
 src="fallback.jpg"
 alt="...">

這裡的 sizes 值會告知瀏覽器,img 佔用的寬度為 80vw,佔可視區域的 80%。請注意,這並非指示,而是在網頁版面配置中說明圖片大小。這與「讓這張圖片佔用可視區域的 80%」,但「當網頁轉譯完成後,這張圖片最終會佔用 80% 的可視區域」。

身為開發人員,您的工作已完成。您已在 srcset 中準確地描述候選來源清單,並在 sizes 中正確描述圖片寬度;和 srcset 中的 x 語法一樣,其餘內容需透過瀏覽器進行。

不過,為了全面瞭解這些資訊的使用方式,讓我們花點時間瞭解使用者的瀏覽器在遇到此標記時,會做出哪些決定:

您已通知瀏覽器,這張圖片佔用了 80% 的可用可視區域:因此,如果我們要在具有 1000 像素寬可視區域的裝置上顯示這個 img,這張圖片就會佔用 800 像素。瀏覽器就會採用這個值,並根據我們在 srcset 中指定的每個圖片來源候選圖片的寬度進行劃分。最小來源的固有大小為 600 像素,所以如下:600÷800=.75。我們的中等圖片寬度為 1200 像素:1200÷800=1.5。我們最大的圖片寬度為 2000 像素:2000÷800=2.5。

這些計算的結果 (.751.52.5) 實際上是專門根據使用者的可視區域大小調整的 DPR 選項。由於瀏覽器也會同時取得使用者的顯示密度資訊,因此會做出一系列的決定:

在這個可視區域大小下,無論使用者的顯示密度為何,系統都會捨棄 small.jpg 候選項目,因為計算的 DPR 低於 1,這個來源需要對任何使用者放大,因此不適用。在 DPR 為 1 的裝置上,medium.jpg 可提供最接近的比對方式,該來源適合於 1.5 的 DPR 顯示,因此稍大大於必要,但請記住,縮小縮放是流暢的視覺流程。在 DPR 為 2 的裝置上,large.jpg 是最接近的值,因此系統會選取該裝置。

如果同一張圖片在 600 像素寬的可視區域中顯示,則計算結果會完全不同:現在 80 vw 是 480px。 當我們將來源的寬度除以該值時,得到 1.252.54.1666666667。在這個可視區域大小下,1x 裝置將會選擇 small.jpgmedium.jpg 則會在 2 倍的裝置上進行比對。

這張圖片在所有瀏覽環境中看起來完全相同:所有來源檔案的尺寸都完全相同,而且每張檔案的算繪顯示密度都會大同小異。不過,系統一定會為使用者提供最小的適當候選者,而不是向每位使用者提供 large.jpg。使用描述性語法而非指示詞時,您無需手動設定中斷點,並考慮未來的可視區域和 DPR,只要為瀏覽器提供資訊,讓瀏覽器判斷答案即可。

sizes 值是相對於可視區域的相對值,且完全與網頁版面配置無關,因此會新增小工具圖層。一般而言,圖片只會佔據特定百分比的可視區域,而且圖片不含任何固定寬度邊界、邊框間距,或來自網頁上其他元素的影響。您經常需要一組單位 (百分比、empx 等) 來表達圖片的寬度。

幸好,在這邊使用 calc(),任何原生支援回應式圖片的瀏覽器也能支援 calc(),讓我們能夠混合及比對 CSS 單位 (例如佔用使用者可視區域完整寬度的圖片,減去 1em 兩側的邊界):

<img
    sizes="calc(100vw-2em)"
    srcset="small.jpg 400w, medium.jpg 800w, large.jpg 1600w, x-large.jpg 2400w"
    src="fallback.jpg"
    alt="...">

說明中斷點

如果您已花很多時間使用回應式版面配置,可能會注意到以下示例中缺少某些部分:版面配置中圖片所佔的空間很有可能在版面配置的中斷點之間改變。在這種情況下,您必須在瀏覽器中傳遞更多詳細資料:sizes 會接受一組以半形逗號分隔的可能圖片轉譯大小候選項目,就像 srcset 會接受以半形逗號分隔的圖片來源候選一樣。這些條件會使用您熟悉的媒體查詢語法。系統會採用第一個比對語法:只要媒體條件相符,瀏覽器就會停止剖析 sizes 屬性,並套用指定的值。

假設您的圖片只佔可視區域的 80%,但在寬度超過 1200 像素的可視區域中,只用了 em 的邊框間距,在可視區域越小的情況下,就能佔滿可視區域的完整寬度。

  <img
     sizes="(min-width: 1200px) calc(80vw - 2em), 100vw"
     srcset="small.jpg 600w, medium.jpg 1200w, large.jpg 2000w"
     src="fallback.jpg"
     alt="...">

如果使用者的可視區域大於 1200 像素,calc(80vw - 2em) 會說明版面配置中的圖片寬度。如果 (min-width: 1200px) 條件「不相符」,瀏覽器會跳到下一個值。由於沒有與這個值相關聯的特定媒體條件,因此系統會使用 100vw 做為預設值。如要使用 max-width 媒體查詢編寫此 sizes 屬性:

  <img
     sizes="(max-width: 1200px) 100vw, calc(80vw - 2em)"
     srcset="small.jpg 600w, medium.jpg 1200w, large.jpg 2000w"
     src="fallback.jpg"
     alt="...">

淺顯易懂的說法:「(max-width: 1200px)嗎?如果未解決,請繼續執行。下一個值 (calc(80vw - 2em)) 沒有符合條件的條件,因此此為選取條件。

您現在已向瀏覽器提供 img 元素的所有相關資訊 (可能的來源、本質的寬度,以及您打算如何向使用者顯示圖片),瀏覽器會使用一組模糊化的規則來決定如何處理這些資訊。如果聽起來不太模糊,那是因為其設計就是這樣。編碼在 HTML 規格中的來源選擇演算法有「明確」模糊的選擇來源,一旦來源、描述元以及圖片轉譯方式均已剖析完畢,瀏覽器就可以自由執行任何動作,而您「無法」知道瀏覽器會選擇哪個來源。

「在高解析度螢幕上使用這個來源」語法是可預測的,但無法解決回應式版面配置中圖片的核心問題:節省使用者頻寬。螢幕的像素密度根本與網際網路連線速度 (如果有) 密切相關。如果您使用家用筆電,但使用計量付費連線、與手機共用網路或使用陰天的 Wi-Fi 連線瀏覽網頁,那麼無論螢幕畫質為何,都建議您停用高解析度圖片來源。

讓瀏覽器保留最後的說法,可以讓瀏覽器的效能明顯提升,遠比使用嚴格規定語法管理得多。舉例來說,在大部分瀏覽器中,使用 srcsetsizes 語法的 img 絕對不會要求尺寸小於使用者瀏覽器快取中維度較小的來源。如果瀏覽器可以順暢地縮小已有的圖片來源資源,那麼對來源發出新要求時,會怎麼樣?但是,如果使用者將可視區域放大到需要新圖片的位置才能避免放大,系統仍會發出「該」要求,讓一切看起來符合預期。

這種缺乏明確的控制項聽起來可能有些可怕,但由於您使用來源檔案的內容相同,因此無論瀏覽器做出的決定為何,使用者都不太可能得到「毀損」的體驗。src

正在使用 sizessrcset

這些資訊很多,對您、讀者和瀏覽器都十分寶貴。srcsetsizes 都是密集語法,會在相對較少的字元中描述大量資訊。也就是說,為了改善或更糟的是,設計設計可以減少語法,幫助人類輕鬆剖析這些語法,使瀏覽器更難以剖析。字串上加入的複雜度越高,瀏覽器間就更有可能出現剖析器錯誤,或發生無意間的行為差異。不過,這樣做的缺點是,機器較容易讀取語法,因此語法較容易撰寫。

srcset 是明確的自動化情況。您很少針對實際工作環境製作多個版本的映像檔,而是使用 Gulp 等工作執行器來自動執行程序;如使用 Webpack 的 Webpack 等套件器、Cloudinary 等第三方 CDN,或所選 CMS 內建的功能。在一開始就產生來源所需的足夠資訊,讓系統有足夠的資訊,可寫入有效的 srcset 屬性。

sizes 的自動化程度稍微高出許多。如您所知,在轉譯的版面配置中,系統唯一能計算圖片大小的方式是「轉譯」版面配置。幸運的是,許多開發人員工具已經彈出許多開發人員工具,簡化了手寫 sizes 屬性的處理程序,使您可能無法手動比對這些屬性。舉例來說,respImageLint 是程式碼片段,用來檢查 sizes 屬性是否正確,並提供改善建議。Lazysizes 專案會將圖片要求延遲到版面配置建立完成後,讓 JavaScript 為您產生 sizes 值,藉此減緩部分效率。如果您使用 React 或 Vue 等完整的用戶端轉譯架構,則有許多編寫和/或產生 srcsetsizes 屬性的解決方案,我們會在 CMS 和架構中進一步說明。