使用 CSS 漸變和縱橫比創建響應式圖像效果

已發表: 2022-03-10
快速總結 ↬ CSS 中的一個經典問題是保持圖像在相關組件(例如卡片)之間的縱橫比。 新支持aspect-ratio屬性與object-fit相結合,為過去令人頭疼的問題提供了一種補救措施! 讓我們學習使用這些屬性,以及創建響應式漸變圖像效果以獲得額外的天賦。

為了準備我們未來的圖像效果,我們將設置一個卡片組件,頂部有一個大圖像,後面是標題和描述。 這種設置的常見問題是我們可能並不總是完美地控製圖像是什麼,更重要的是我們的佈局,它的尺寸是什麼。 雖然這可以通過提前裁剪來解決,但由於容器大小的響應,我們仍然會遇到問題。 結果是卡片內容的位置不均勻,當你出示一排卡片時,它真的很突出。

除了裁剪之外,另一個先前的解決方案可能是從內聯img交換到一個空白div ,該 div 僅存在於通過background-image呈現圖像。 過去我自己多次實施過這個解決方案。 這樣做的一個優點是使用一種較舊的縱橫比技巧,該技巧使用零高度元素並設置padding-bottom值。 將填充值設置為百分比會產生相對於元素寬度的最終計算值。 您可能還使用這個想法來保持視頻嵌入的 16:9 比例,在這種情況下,填充值可以通過以下公式找到: 9/16 = 0.5625 * 100% = 56.26% 。 但是我們將探索兩個不涉及額外數學的現代 CSS 屬性,給我們更多的靈活性,並且還允許通過使用真實的img而不是空的div來保持語義。

首先,讓我們定義 HTML 語義,包括使用無序列表作為卡片的容器:

 <ul class="card-wrapper"> <li class="card"> <img src="" alt=""> <h3>A Super Wonderful Headline</h3> <p>Lorem ipsum sit dolor amit</p> </li> <!-- additional cards --> </ul>

接下來,我們將為.card組件創建一組最小的基線樣式。 我們將為卡片本身設置一些基本的視覺樣式,快速更新預期的h3標題,然後開始設置卡片圖像樣式的基本樣式。

 .card { background-color: #fff; border-radius: 0.5rem; box-shadow: 0.05rem 0.1rem 0.3rem -0.03rem rgba(0, 0, 0, 0.45); padding-bottom: 1rem; } .card > :last-child { margin-bottom: 0; } .card h3 { margin-top: 1rem; font-size: 1.25rem; } img { border-radius: 0.5rem 0.5rem 0 0; width: 100%; } img ~ * { margin-left: 1rem; margin-right: 1rem; }

最後一條規則使用通用兄弟組合器為img之後的任何元素添加水平邊距,因為我們希望圖像本身與卡片的側面齊平。

到目前為止,我們的進展導致我們出現以下卡片外觀:

一張卡片應用了之前描述的基線樣式,其中包括一張來自 Unsplash 的圖片:小盤子上的甜點,旁邊是杯子裡的熱飲
一張具有先前描述的基線樣式的卡片應用並包括來自 Unsplash 的圖像,該圖像在小盤子上的甜點旁邊是杯子裡的熱飲料。 (大預覽)

最後,我們將使用 CSS 網格創建快速響應佈局的.card-wrapper樣式。 這也將刪除默認列表樣式。

 .card-wrapper { list-style: none; padding: 0; margin: 0; display: grid; grid-template-columns: repeat(auto-fit, minmax(30ch, 1fr)); grid-gap: 1.5rem; }

注意如果您不熟悉這種網格技術,請查看我的教程中關於 12 列網格的現代解決方案的說明。

應用了這個並且所有卡片都包含具有有效源路徑的圖像,我們的.card-wrapper樣式為我們提供了以下佈局:

由於應用了卡片包裝佈局樣式,三張卡片連續顯示。每張卡片都有一個獨特的圖像,具有不同的自然縱橫比,最後一張卡片的垂直方向圖像是其他卡片圖像高度的兩倍以上
由於應用了卡片包裝佈局樣式,三張卡片連續顯示。 每張卡片都有一個獨特的圖像,具有不同的自然縱橫比,最後一張卡片的垂直方向圖像是其他卡片圖像高度的兩倍以上。 (大預覽)

如預覽圖像所示,這些基線樣式不足以正確包含圖像,因為它們具有不同的自然尺寸。 我們需要一種方法來統一和一致地約束這些圖像。

跳躍後更多! 繼續往下看↓

啟用具有object-fit統一圖像大小

如前所述,您之前可能已在此場景中進行了更新,以更改要通過background-image添加的圖像,並使用background-size: cover來很好地調整圖像大小。 或者您可能已經嘗試提前強制裁剪(這仍然是一個有價值的目標,因為任何圖像尺寸減小都會提高性能!)。

現在,我們有了object-fit屬性,它可以讓img標籤充當圖像的容器。 並且,它還帶有一個cover值,可以產生與背景圖像解決方案類似的效果,但具有保留內聯圖像語義的好處。 讓我們應用它,看看它是如何工作的。

我們確實需要將它與height尺寸配對,以獲得關於我們希望圖像容器如何表現的額外指導(回想一下我們已經添加了width: 100% )。 我們將使用max()函數來選擇10rem30vh ,具體取決於給定上下文中哪個更大,這可以防止圖像高度在較小的視口用戶設置大縮放時縮小太多。

 img { /* ...existing styles */ object-fit: cover; height: max(10rem, 30vh); }

額外的輔助功能提示您應該始終在桌面上使用 200% 和 400% 縮放來測試您的佈局。 雖然目前沒有zoom媒體查詢,但max()等函數可以幫助解決佈局問題。 該技術有用的另一個上下文是元素之間的間距。

通過這次更新,我們確實改進了一些東西,視覺效果就好像我們使用了舊的背景圖像技術:

三張卡片的圖像現在看起來具有統一的高度,並且圖像內容在圖像中居中,就好像它是一個容器一樣
三張卡片的圖像現在看起來具有統一的高度,並且圖像內容在圖像中居中,就好像它是一個容器一樣。 (大預覽)

響應一致的圖像大小與aspect-ratio

當單獨使用object-fit時,一個缺點是我們仍然需要設置一些維度提示。

即將推出的屬性(目前在 Chromium 瀏覽器中可用)稱為aspect-ratio ,將增強我們一致地調整圖像大小的能力。

使用這個屬性,我們可以定義一個比率來調整圖像的大小,而不是設置明確的尺寸。 我們將繼續將它與object-fit結合使用,以確保這些尺寸僅影響作為容器的圖像,否則,圖像可能會出現失真。

這是我們完整更新的圖像規則:

 img { border-radius: 0.5rem 0.5rem 0 0; width: 100%; object-fit: cover; aspect-ratio: 4/3; }

對於卡片上下文,我們將從43的圖像比例開始,但您可以選擇任何比例。 例如, 11表示正方形,或169表示標準視頻嵌入。

這是更新後的卡片,儘管在這個特定實例中可能很難注意到視覺差異,因為縱橫比恰好與我們通過單獨設置object-fit height獲得的外觀非常匹配。

三張卡片的圖像具有相同的寬度和高度尺寸,這與之前的 object-fit 解決方案略有不同
三張卡片圖像具有相同的寬度和高度尺寸,這與之前的對象擬合解決方案略有不同。 (大預覽)

設置 `aspect-ratio` 會導致比例隨著元素的增長或縮小而保持不變,而當僅設置 `object-fit` 和 `height` 時,圖像比例將隨著容器尺寸的變化而不斷變化。

使用 CSS 漸變和函數添加響應式效果

好的,現在我們知道瞭如何設置一致大小的圖像,讓我們通過添加漸變效果來享受它們的樂趣!

我們使用此效果的目標是讓它看起來好像圖像正在淡入卡片內容。 您可能很想將圖像包裝在自己的容器中以添加漸變,但是由於我們已經在圖像大小方面所做的工作,我們可以弄清楚如何在主.card上安全地執行此操作。

第一步是定義梯度。 我們將使用 CSS 自定義屬性添加漸變顏色,以便輕鬆交換漸變效果,從藍色到粉紅色開始。 漸變中的最後一種顏色將始終為白色,以保持過渡到卡片內容背景並創建“羽化”邊緣。

 .card { --card-gradient: #5E9AD9, #E271AD; background-image: linear-gradient( var(--card-gradient), white max(9.5rem, 27vh) ); /* ...existing styles */ }

但是等等——這是一個 CSS max()函數嗎? 在漸變中? 是的,這是可能的,而且它是使這個漸變響應有效的魔力!

但是,如果我要添加屏幕截圖,我們實際上不會看到漸變對圖像有任何影響。 為此,我們需要引入mix-blend-mode屬性,在這種情況下,我們將使用overlay值:

 img { /* ...existing styles */ mix-blend-mode: overlay; }

mix-blend-mode屬性類似於應用 Photoshop 等照片處理軟件中可用的圖層混合樣式。 並且overlay值將具有允許圖像中的中等色調與其後面的漸變混合的效果,從而導致以下結果:

每張卡片圖像都有漸變混合效果,從頂部的淺藍色開始,混合到紅粉紅色,然後在卡片文本內容的其餘部分之前羽化為白色
每張卡片圖像都有一個漸變混合效果,從頂部的淺藍色開始,混合到微紅色的粉紅色,然後在卡片文本內容的其餘部分之前羽化成白色。 (大預覽)

現在,此時,我們僅依靠aspect-ratio來調整圖像大小。 如果我們調整容器大小並導致卡片佈局重排,則不斷變化的圖像高度會導致漸變漸變為白色的位置不一致。

因此,我們還將添加一個max-height屬性,該屬性使用max()函數並包含比漸變中的值稍大的值。 由此產生的行為是漸變將(幾乎總是)正確地與圖像底部對齊。

 img { /* ...existing styles */ max-height: max(10rem, 30vh); }

需要注意的是,添加 `max-height` 會改變 `aspect-ratio` 行為。 它不會總是使用精確的比例,而是僅在給定“最大高度”的新額外約束時有足夠的分配空間時使用。

但是, aspect-ratio仍將繼續確保圖像大小一致地調整,這與僅object-fit相比具有優勢。 嘗試在最終的 CodePen 演示中註釋掉aspect-ratio ,以查看它在不同容器大小之間的差異。

由於我們最初的目標是實現一致的響應式圖像尺寸,因此我們仍然達到了目標。 對於您自己的用例,您可能需要調整比率和高度值以達到您想要的效果。

替代方案: mix-blend-mode並添加過濾器

使用overlay作為mix-blend-mode值是我們正在尋找的淡入白效果的最佳選擇,但讓我們嘗試另一種選擇以獲得更戲劇性的效果。

我們將更新我們的解決方案,為mix-blend-mode值添加 CSS 自定義屬性,並更新漸變的顏色值:

 .card { --card-gradient: tomato, orange; --card-blend-mode: multiply; } img { /* ...existing styles */ mix-blend-mode: var(--card-blend-mode); }

multiply值對中間色調有暗化效果,但保持白色和黑色不變,從而產生以下外觀:

每張卡片圖像都具有從紅橙色到純橙色的新漸變的強烈橙色色調。白色區域仍然是白色,黑色區域仍然是黑色
每張卡片圖像都具有從紅橙色到純橙色的新漸變的強烈橙色色調。 白色區域仍然是白色,黑色區域仍然是黑色。 (大預覽)

雖然我們已經失去了淡入淡出並且現在在圖像底部有一個硬邊,但我們漸變的白色部分仍然很重要,以確保漸變在卡片內容之前結束。

我們可以添加的另一個修改是使用filter ,特別是使用grayscale()函數來去除圖像顏色,因此漸變是圖像著色的唯一來源。

 img { /* ...existing styles */ filter: grayscale(100); }

使用grayscale(100)的值會導致完全去除圖像的自然顏色並將其轉換為黑白。 這是與之前使用我們的橙色漸變和multiply效果的屏幕截圖進行比較的更新:

現在每個卡片圖像仍然具有橙色漸變,但所有其他顏色都被刪除並被灰色陰影代替
現在每個卡片圖像仍然具有橙色漸變,但所有其他顏色都被移除並被灰色陰影取代。 (大預覽)

使用aspect-ratio增強

如前所述,目前僅最新版本的 Chromium 瀏覽器(Chrome 和 Edge)支持aspect-ratio 。 然而,所有的瀏覽器都支持object-fit ,再加上我們的height限制,導致了一個不太理想但仍然可以接受的結果,在這裡可以看到 Safari:

卡片圖像高度有上限,但每張卡片的實際高度略有不同
卡片圖像高度有上限,但每張卡片的實際高度略有不同。 (大預覽)

如果沒有aspect-ratio功能,這裡的結果是最終圖像高度被限制,但每個圖像的自然尺寸仍然導致卡片圖像高度之間的一些差異。 您可能希望改為添加max-height或再次使用max()函數來幫助使max-height卡片尺寸下更具響應性。

擴展漸變效果

由於我們將漸變色標定義為 CSS 自定義屬性,因此我們可以隨時在不同的上下文中更改它們。 例如,如果卡片懸停或其中一個子元素處於焦點位置,我們可能會更改漸變以更強烈地顯示其中一種顏色。

首先,我們將更新每張卡片h3以包含一個鏈接,例如:

 <h3><a href="">A Super Wonderful Headline</a></h3>

然後,我們可以使用我們最新的可用選擇器之一 - :focus-within - 在鏈接處於焦點時更改卡片漸變。 為了額外覆蓋可能的交互,我們將把它與:hover結合起來。 而且,我們將重用我們的max()想法來分配一種顏色來接管卡片圖像部分的覆蓋範圍。 這種特殊效果的缺點是漸變停止和顏色變化不能可靠地動畫化——但由於 CSS Houdini,它們很快就會實現。

要更新顏色並添加新的色標,我們只需要在這個新規則中重新分配--card-gradient的值:

 .card:focus-within, .card:hover { --card-gradient: #24a9d5 max(8.5rem, 20vh); }

我們的max()值小於用於white以保持羽化邊緣的原始值。 如果我們使用相同的值,它將遇到white並創建一個清晰的直尺分離。

在創建這個演示時,我最初嘗試了一個使用帶有scaletransform來實現放大效果的效果。 但我發現,由於應用了mix-blend-mode ,瀏覽器不會始終如一地重新繪製圖像,從而導致令人不快的閃爍。 請求瀏覽器執行純 CSS 效果和動畫總是需要權衡取捨,雖然我們可以做的很酷,但最好檢查效果對性能的影響。

體驗愉快!

現代 CSS 為我們提供了一些很棒的工具來更新我們的網頁設計工具包,其中最新添加的aspect-ratio 。 因此,繼續嘗試object-fitaspect-ratio ,並在漸變中添加諸如max()之類的函數,以獲得一些有趣的響應效果! 請務必仔細檢查跨瀏覽器(現在!)以及不同的視口和容器大小。

這是 CodePen,包括我們今天回顧的功能和效果:

請參閱 Stephanie Eckles 的 Pen [Responsive Image Effects with CSS Gradients and aspect-ratio](https://codepen.io/smashingmag/pen/WNoERXo)。

請參閱 Stephanie Eckles 的具有 CSS 漸變和縱橫比的 Pen Responsive Image Effects。

尋找更多? 確保您在 Smashing 上查看我們的 CSS 指南 →