[譯] 使用 CSS 網格佈局實現響應式圖片

LucasTwilight發表於2019-03-31

使用網格佈局實現響應式圖片

當我第一次將一張圖片響應式地展示在頁面中的時候,我使用的是下面四行非常簡單的程式碼:

img {
  max-width: 100%;
  height auto; /* default */
}
複製程式碼

雖然,對我這樣一個開發人員來說是有效的,但是對於使用者來說,卻不是非常友好。如果 src 中的圖片檔案較大呢?在開發人員的高階裝置上(比如,我的具有 16 GB RAM 的裝置),很少甚至從不出現效能問題。但是在低端裝置上呢?情況就不一樣了。

image at multiple screen sizes

上面的插圖不是非常詳細。我來自奈及利亞,如果你需要你的產品能夠在非洲工作,那麼你不應該看上面的那張圖。請看下面這張圖片

[譯] 使用 CSS 網格佈局實現響應式圖片

如今,價格最低的 iPhone 售價平均為 300 美元。即使 iPhone 是衡量快速裝置的門檻,普通非洲人也買不起。

首先,如果你能夠理解上面的智慧手機的產業分析,你就能夠明白為什麼設定 CSS 的寬度並不是實現響應式圖片的好辦法了。你會問為什麼?請讓我先來解釋一下影象的含義。

影象的細微差別

影象對於使用者是非常有吸引力的,但是對於我們開發者來說是一項間距的任務,因為我們必須考慮到下面的因素:

  • 格式
  • 影象佔用磁碟大小
  • 渲染尺寸(瀏覽器中的佈局高度和寬度)
  • 原始尺寸(原始圖片的高度和寬度)
  • 寬高比

那麼,我們如何選擇正確的引數,並且巧妙地混合和匹配上面的幾個因素,來為使用者提供最佳的體驗呢?反過來說,最終的答案取決於下面幾個問題的答案。

  • 影象是由使用者動態建立的還是由視覺設計團隊靜態建立的?
  • 如果影象的寬度和高度不成比例地改變,會影響影象的質量嗎?
  • 所有的影象都以相同的寬度和高度渲染嗎?渲染的時候,這些影象必須有特定的寬高比還是完全不同寬高比?
  • 在不同視口上呈現影象的時候,必須要考慮什麼?

記下你的答案。它們不僅僅可以幫助您瞭解自己的影象 —— 它們的來源,技術要求等 —— 還可以讓您在交付的時候,能夠做出正確的選擇。

影象傳遞時的臨時策略

影象的傳遞已經從簡單的 URL 新增到 src 屬性轉變到更加複雜的場景下了。在深入研究它們之前,讓我們先談談用於呈現影象的多種選項,以便您在可以指定策略,選擇在何時以及如何傳遞和渲染影象。

首先,需要確定影象的來源。這樣可以對於部分因為不好分類或者來源不清而難以處理的影象來確定其處理方式,並且可以儘可能有效地處理影象。

通常,影象是屬於以下兩者之一的:

  • 動態的:動態影象是使用者自己上傳的,或者由系統中的其他事件生成。
  • 靜態的:攝影師,設計師,或者您自己(開發人員)為網站建立的。

讓我們深入研究一下針對每種型別的影象的策略。

動態影象策略

靜態影象非常容易使用。相較而言,動態影象比較棘手並且容易出現問題。可以通過什麼方法來弱化他們的動態特性,並且使其像靜態影象一樣更加可控呢?有兩個途徑:校驗智慧裁剪

校驗

為使用者制定一些對於影象的限制規則,來向使用者說明我們可以接收什麼型別的影象或者不能接收什麼型別的影象。現在,我們可以驗證影象的所有屬性,也就是:

  • 格式
  • 影象佔用空間的大小
  • 尺寸
  • 寬高比

**注意:**渲染尺寸是在渲染的過程中確定的,所以沒有必要驗證這一項。

在驗證過後,我們可以得到一系列的可預測的影象,這些影象會變得更加易於使用。

智慧裁剪

處理動態影象的另外一種策略是智慧地裁剪這些影象,來避免刪除掉重要的內容,並且將影象的主要內容著重展示出來。這很難做到。但是,您可以利用開源的工具或者專門從事影象管理的 SaaS 公司提供的人工智慧服務。下面是一個例子。


一旦為動態影象確定了策略,請建立一個包含影象的所有佈局選項的規則表。以下是一個例子,這個例子非常值得分析,來幫你確定最重要的裝置以及視口大小。

[譯] 使用 CSS 網格佈局實現響應式圖片

最(次)低限度

現在拋開響應式的複雜性,並且做我們最為擅長的事情 —— 使用 HTML 程式碼結合使用了最大寬度的 CSS。

下面的程式碼會渲染一些圖片:

<main>
  <figure>
    <img src="https://res.cloudinary.com/...w700/ps4-slim.jpg" alt="PS4 Slim">
  </figure>
      
  <figure>
    <img src="https://res.cloudinary.com/...w700/x-box-one-s.jpg" alt="X Box One S">
  </figure>
      
  <!-- More images -->
    
  <figure>
    <img src="https://res.cloudinary.com/...w700/tv.jpg" alt="Tv">
  </figure>
</main>
複製程式碼

**注意:**影象 URL 中的省略號指定了資料夾,影象的尺寸和裁剪策略,這些資訊包含了太多細節,因此對於其進行了截斷,讓我們將關注點集中到重要的地方。上述程式碼完整的版本,可以參閱下面的 CodePen 示例。

這是網路上能夠找到的最短示例,能夠讓影象變成響應式的:

/* The parent container */
main {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
}

img {
  max-width: 100%;
}
複製程式碼

假如影象的寬度和高度是可變的,那麼請將 max-width 替換為 object-fit,並且將屬性值設為 cover

[譯] 使用 CSS 網格佈局實現響應式圖片

Jo Franchetti 的部落格使用 CSS Grid 的常見響應式佈局中解釋了 grid-template-columns 屬性是如何使整個佈局自適應(響應式)的。

可以參閱 CodePen 上,Chris Nwamba (@codebeast) 的這段程式碼:Grid 相簿

然而,以上的內容並不是我們想要的,因為……

  • 高階和低端裝置的影象尺寸和大小相同,並且
  • 我們可能會希望對於影象的寬度更加嚴格,而不是將其設定為 250,並且讓其不斷增大。

好吧,這部分只涵蓋了“最低限度”,所以做到這樣就夠了。

佈局變化

影象的佈局發生變化的時候,最糟糕的事情就是影象的渲染髮生垮塌。由於影象可能具有不同的尺寸(寬度和高度),因此我們需要指定如何去渲染影象。

我們應該智慧地將所有影象裁剪成統一的尺寸嗎?我們應該保留視口的寬高比,並且改變另外一個視口的比例嗎?這應該由我們自己決定。

在網格佈局中的影象,比如上述示例中的具有不同寬高比的影象,我們可以視覺適配(art direction)方面的技術來渲染影象,視覺適配可以幫助我們來實現這樣的目標:

[譯] 使用 CSS 網格佈局實現響應式圖片

有關解析度切換和響應式影象中的視覺適配,請閱讀 Jason Grigsby 的一系列文章。另外一個資訊豐富的參考資料是 Eric Portis 的響應式影象指南,第一部分第二部分,以及第三部分

讓我們看一下下面的程式碼示例。

<main>
  <figure>
    <picture>
      <source media="(min-width: 900px)" srcset="https://res.cloudinary.com/.../c_fill,g_auto,h_1400,w_700/camera-lens.jpg">
      
      <img src="https://res.cloudinary.com/.../c_fill,g_auto,h_700,w_700/camera-lens.jpg" alt="Camera lens">
    </picture>
  </figure>
  
  <figure>
    <picture>
      <source media="(min-width: 700px)" srcset="https://res.cloudinary.com/.../c_fill,g_auto,h_1000,w_1000/ps4-pro.jpg"></source>
    </picture>
    <img src="https://res.cloudinary.com/.../c_fill,g_auto,h_700,w_700/ps4-pro.jpg" alt="PS4 Pro">
  </figure>
</main>
複製程式碼

我們僅在視口寬度超過 700 px 的時候,渲染 700 px x 700 px 的影象,而不是簡單的渲染 700 px 寬度的影象。如果視口較大,則會進行下面的渲染:

  • 相機鏡頭的影象會被渲染為 700 px 寬、1000 px 高的影象(700 px x 1000 px)。
  • PS4 Pro 的影象會被渲染為 1000 px x 1000 px。

視覺適配

通過裁剪影象使其變成響應式,我們可能會在無意之中裁減掉影象中的重要內容,例如主體的面部。如前所述,AI 開源工具可以輔助智慧裁剪,並且重新聚焦於影象中的主要物件。此外,Nadav Soferman 關於智慧裁剪的文章是非常有用的入門指南。

嚴格的網格和跨度

本文中關於響應式影象的第一個例子是比較靈活的。在寬度至少有 300 px 的情況下,網格項會根據視口寬度自動適配,這樣很不錯。

另一方面,我們可能希望根據設計規範對網格項使用更加嚴格的規則。在這種情況下,媒體查詢就會排上用場了。

或者,我們可以利用 grid-span 功能建立不同寬度和長度的網格項。

@media(min-width: 700px) {
  main {
    display: grid;
    grid-template-columns: repeat(2, 1fr);
  }
}

@media(min-width: 900px) {
  main {
    display: grid;
    grid-template-columns: repeat(3, 1fr)
  }
  figure:nth-child(3) {
    grid-row: span 2;
  }
  figure:nth-child(4) {
    grid-column: span 2;
    grid-row: span 2;
  }
}
複製程式碼

對於寬視口中 1000 px x 1000 px 的正方形影象,我們可以讓其在行和列上分別跨越兩個網格單元。在更寬的視口上,更改為縱向(700 px x 1000 px)的影象,使其跨越兩行。

可以參閱 CodePen 上,Chris Nwamba (@codebeast) 的這段程式碼:網格相簿 [視覺適配]

漸進式優化

盲目的優化與沒有優化一樣蹩腳。如果沒有事先的衡量,請不要專注於優化。如果優化沒有資料的支援,請不要進行優化。

儘管如此,在上述的示例中,有足夠的空間允許你去優化。我們從最低限度開始,向您展示了一些蠻酷的技巧,現在我們有了一個可以正常工作的響應式網格佈局。接下來的問題是,“如果頁面包含 20 - 100 個影象,那麼使用者體驗會是什麼樣的呢?”

答案是這樣的:我們必須要保證在進行大量影象渲染的時候,影象的大小適合於渲染它們的裝置。為此,我們需要指定多個影象的 URL,而不是一個。瀏覽器將根據判斷標準選擇正確(最優)的一個。該技術在響應式影象中稱為解析度切換。我們可以關注下面的這個程式碼示例:

<img 
  srcset="https://res.cloudinary.com/.../h_300,w_300/v1548054527/ps4.jpg 300w,
          https://res.cloudinary.com/.../h_700,w_700/v1548054527/ps4.jpg 700w,
          https://res.cloudinary.com/.../h_1000,w_1000/v1548054527/ps4.jpg 1000w"
             
  sizes="(max-width: 700px) 100vw, (max-width: 900px) 50vw, 33vw"
  src="https://res.cloudinary.com/.../h_700,w_700/v1548054527/ps4.jpg 700w"
  alt="PS4 Slim">
複製程式碼

Harry Roberts 的推文直觀地解釋了會發生什麼(譯者注:考慮到沒有科學上網的小夥伴,相關 tweet 會整理到附錄中):

我發現(到目前為止)提取/解釋 srcsetsizes 的最簡單的方法是:pic.twitter.com/I6YW0AqKfM(譯者注:圖片見底部附錄 A

— Harry Roberts (@csswizardry) March 1, 2017

當我第一次嘗試解析度切換的時候,我感到了困惑,並且發了下面的推文:

我知道 img 標籤的 srcset 屬性中的寬度描述符表示影象的源寬度(原始檔案寬度)。

我不知道的是,為什麼需要這個屬性?瀏覽器根據這個值做了什麼? cc @grigs ?

— Christian Nwamba (@codebeast) October 5, 2018

向 Jason Grigsby 在回覆中的解釋致敬(譯者注:Jason Grigsby 的回覆見底部附錄 B)。

由於解析度的切換,如果調整瀏覽器大小,則會針對對應的視口大小,下載對應大小的影象;因此小型手機會下載小影象(這樣對於 CPU 和 RAM 來說都很好),而大型視口則會下載較大的影象。

[譯] 使用 CSS 網格佈局實現響應式圖片

上表中顯示瀏覽器下載了具有不同磁碟大小(紅色框)的相同影象(藍色框)。

可以參閱 CodePen 上,Chris Nwamba (@codebeast) 的這段程式碼:網格相簿 [優化]

Cloudinary 的開源、免費的響應式影象斷點生成器可以將網站的影象調整為多種螢幕尺寸。但是,在許多情況下,單獨設定 srcsetsizes 就足夠了。

結論

本文旨在提供一種簡單並且有效的指導原則,以便從多種可用的選項設定中,找到可以進行響應式影象佈局的屬性。熟悉 CSS 網格,視覺適配和解析度切換三種方法,你將在短暫的時間內,成為一名高手。請保持練習。

附錄

附錄 A

下圖為 @Harry Roberts 對於 srcsetsizes 屬性的解釋。

對於 srcset 和 sizes 屬性的解釋

附錄 B

下面是 Jason Grigsby 對於作者 twitter 的回覆內容,原回覆請看文中給到的相關連結。

我認為是這樣的,圖片的寬度描述符是一個瀏覽器可以選擇的陣列。 你不是在告訴瀏覽器去選擇什麼,而是在告訴瀏覽器選項是什麼。

它就像貨架上面的一堆產品。你要把消費者選擇產品時會用到的特徵列出來。 在這種情況下,消費者是瀏覽器,它需要知道的特徵是影象的寬度。

所以 srcset 不是規定性的,我們只是提供一系列選項。瀏覽器是如何在影象之間選擇的呢? 這就是 sizes 屬性的來源。它允許瀏覽器根據當前的視口計算頁面上影象框的大小。

我們可以這樣想象,當瀏覽器站在一堆影象前,使用 sizes 屬性計算影象在頁面中的大小,然後可能會考慮網路速度以及其他因素,然後說:“我將會選擇第二張圖片,非常感謝。”

— Jason Grigsby


如果發現譯文存在錯誤或其他需要改進的地方,歡迎到 掘金翻譯計劃 對譯文進行修改並 PR,也可獲得相應獎勵積分。文章開頭的 本文永久連結 即為本文在 GitHub 上的 MarkDown 連結。


掘金翻譯計劃 是一個翻譯優質網際網路技術文章的社群,文章來源為 掘金 上的英文分享文章。內容覆蓋 AndroidiOS前端後端區塊鏈產品設計人工智慧等領域,想要檢視更多優質譯文請持續關注 掘金翻譯計劃官方微博知乎專欄

相關文章