首頁 > web前端 > css教學 > 獲得CSS梯度陰影的不同方法

獲得CSS梯度陰影的不同方法

Lisa Kudrow
發布: 2025-03-09 10:03:14
原創
426 人瀏覽過

Different Ways to Get CSS Gradient Shadows

經常有人問我這樣一個問題:能否用漸變色而不是純色來創建陰影? CSS中沒有專門的屬性可以實現這一點(相信我,我已經找過了),任何你找到的關於這方面的博文基本上都是一些近似漸變的CSS技巧。我們接下來會介紹其中一些技巧。

但首先……又是關於漸變陰影的文章?真的嗎?

是的,這又是關於這個主題的另一篇文章,但它有所不同。我們將一起突破極限,尋找一個解決方案,涵蓋我從未在其他地方見過的內容:透明度。大多數技巧在元素具有非透明背景時有效,但如果我們有透明背景怎麼辦?我們將在此探討這種情況!

在我們開始之前,讓我介紹一下我的漸變陰影生成器。你只需調整配置,即可獲得代碼。但請繼續閱讀,因為我將幫助你理解生成代碼背後的所有邏輯。

目錄

  • 非透明解決方案
  • 透明解決方案
  • 添加圓角
  • 總結

非透明解決方案

讓我們從適用於80% 常見情況的解決方案開始。最典型的情況是:你正在使用一個帶有背景的元素,並且需要向其添加漸變陰影。這裡不需要考慮透明度問題。

解決方案是依靠一個定義了漸變的偽元素。你把它放在實際元素的後面,並對其應用模糊濾鏡。

<code>.box {
  position: relative;
}
.box::before {
  content: "";
  position: absolute;
  inset: -5px; /* 控制扩散 */
  transform: translate(10px, 8px); /* 控制偏移量 */
  z-index: -1; /* 将元素置于后面 */
  background: /* 你的渐变色在这里 */;
  filter: blur(10px); /* 控制模糊 */
}</code>
登入後複製
登入後複製
登入後複製
登入後複製
登入後複製

代碼看起來很多,這是因為它確實很多。以下是如何使用box-shadow代替它,如果我們使用純色而不是漸變色的話。

<code>box-shadow: 10px 8px 10px 5px orange;</code>
登入後複製
登入後複製
登入後複製
登入後複製
登入後複製

這應該讓你很好地了解第一段代碼中值的作用。我們有X和Y偏移量、模糊半徑和擴散距離。請注意,我們需要一個來自inset屬性的負擴散距離值。

這是一個演示,顯示了漸變陰影與經典box-shadow並排:

如果你仔細觀察,你會注意到兩個陰影略有不同,尤其是模糊部分。這並不奇怪,因為我很確定filter屬性的算法與box-shadow的算法不同。這沒什麼大不了的,因為最終結果非常相似。

這個解決方案很好,但仍然有一些與z-index:-1聲明相關的缺點。是的,那裡發生了“堆疊上下文”!

我向主元素應用了一個轉換,然後,陰影不再位於元素下方。這不是錯誤,而是堆疊上下文的邏輯結果。別擔心,我不會開始對堆疊上下文進行枯燥的解釋(我已經在Stack Overflow線程中做過這個了),但我仍然會向你展示如何解決它。

我推薦的第一個解決方案是使用3D轉換:

<code>.box {
  position: relative;
}
.box::before {
  content: "";
  position: absolute;
  inset: -5px; /* 控制扩散 */
  transform: translate(10px, 8px); /* 控制偏移量 */
  z-index: -1; /* 将元素置于后面 */
  background: /* 你的渐变色在这里 */;
  filter: blur(10px); /* 控制模糊 */
}</code>
登入後複製
登入後複製
登入後複製
登入後複製
登入後複製

我們不使用z-index: -1,而是使用沿Z軸的負平移。我們將所有內容都放在translate3d()中。不要忘記在主元素上使用transform-style: preserve-3d;否則,3D轉換將不會生效。

據我所知,這個解決方案沒有任何副作用……但也許你會看到一個。如果是這樣,請在評論區分享,讓我們嘗試找到一個解決方法!

如果由於某種原因你無法使用3D轉換,另一個解決方案是依靠兩個偽元素——::before和::after。一個創建漸變陰影,另一個複制主背景(以及你可能需要的其他樣式)。這樣,我們可以輕鬆控制兩個偽元素的堆疊順序。

<code>box-shadow: 10px 8px 10px 5px orange;</code>
登入後複製
登入後複製
登入後複製
登入後複製
登入後複製

重要的是要注意,我們正在通過在其上聲明z-index: 0或任何其他執行相同操作的屬性來強制主元素創建堆疊上下文。此外,不要忘記偽元素將主元素的填充框視為參考。因此,如果主元素有邊框,則在定義偽元素樣式時需要考慮這一點。你會注意到我在::after上使用inset: -2px來考慮在主元素上定義的邊框。

正如我所說,只要你不需要支持透明度,這個解決方案在大多數情況下可能就足夠好了,你想要一個漸變陰影。但我們是為了挑戰和突破極限而來的,所以即使你不需要接下來要講的內容,也請繼續關注。你可能會學習到可以在其他地方使用的新的CSS技巧。

透明解決方案

讓我們從我們在3D轉換中結束的地方開始,並從主元素中移除背景。我將從一個偏移量和擴散距離都等於0的陰影開始。

其想法是找到一種方法來裁剪或隱藏元素區域(綠色邊框內)內的所有內容,同時保留外部內容。我們將為此使用clip-path。但你可能會想知道clip-path如何在一個元素內部進行裁剪。

確實,沒有辦法做到這一點,但是我們可以使用特定的多邊形圖案來模擬它:

<code>.box {
  position: relative;
  transform-style: preserve-3d;
}
.box::before {
  content: "";
  position: absolute;
  inset: -5px;
  transform: translate3d(10px, 8px, -1px); /* (X, Y, Z) */
  background: /* .. */;
  filter: blur(10px);
}</code>
登入後複製
登入後複製
登入後複製

瞧!我們得到了一個支持透明度的漸變陰影。我們所做的只是向之前的代碼添加了一個clip-path。這裡有一張圖來說明多邊形部分。

藍色區域是在應用clip-path後可見的部分。我只使用藍色來說明這個概念,但實際上,我們只會看到該區域內的陰影。正如你所看到的,我們定義了四個點,它們的值很大(B)。我的大值是100vmax,但它可以是你想要的任何大值。其想法是確保我們有足夠的陰影空間。我們還有四個點是偽元素的角點。

箭頭表示定義多邊形的路徑。我們從(-B, -B)開始,直到到達(0,0)。總共我們需要10個點。不是8個點,因為路徑中重複了兩次兩個點(( -B,-B)和(0,0))。

我們還需要做最後一件事,那就是考慮擴散距離和偏移量。上面演示之所以有效,僅僅是因為這是一個特殊情況,偏移量和擴散距離都等於0。

讓我們定義擴散並看看會發生什麼。記住,我們使用負值的inset來做到這一點:

偽元素現在比主元素大,所以clip-path裁剪的內容比我們需要多。記住,我們總是需要裁剪主元素內部的部分(示例中綠色邊框內的區域)。我們需要調整clip-path中四個點的位。

<code>.box {
  position: relative;
}
.box::before {
  content: "";
  position: absolute;
  inset: -5px; /* 控制扩散 */
  transform: translate(10px, 8px); /* 控制偏移量 */
  z-index: -1; /* 将元素置于后面 */
  background: /* 你的渐变色在这里 */;
  filter: blur(10px); /* 控制模糊 */
}</code>
登入後複製
登入後複製
登入後複製
登入後複製
登入後複製

我們為擴散距離定義了一個CSS變量--s,並更新了多邊形點。我沒有觸及我使用大值的地方。我只更新定義偽元素角的點。我將所有零值增加--s,並將100%的值減少--s。

偏移量也是同樣的邏輯。當我們轉換偽元素時,陰影會錯位,我們需要再次校正多邊形並將點移動到相反的方向。

<code>box-shadow: 10px 8px 10px 5px orange;</code>
登入後複製
登入後複製
登入後複製
登入後複製
登入後複製

還有兩個變量用於偏移量:--x和--y。我們在transform內部使用它們,我們還更新了clip-path值。我們仍然不觸及具有大值的多邊形點,但我們偏移所有其他點——我們從X坐標中減少--x,從Y坐標中減少--y。

現在我們只需要更新一些變量來控制漸變陰影。當我們這樣做的時候,讓我們也把模糊半徑也變成一個變量:

我們還需要3D轉換技巧嗎?

這完全取決於邊框。不要忘記偽元素的參考是填充框,因此如果你對主元素應用邊框,你將會有重疊。你可以保留3D轉換技巧或更新inset值來考慮邊框。

這是前面演示中用更新的inset值代替3D轉換的版本:

我認為這是一種更合適的方法,因為擴散距離將更準確,因為它從border-box而不是padding-box開始。但是你需要根據主元素的邊框調整inset值。有時,元素的邊框是未知的,你必須使用之前的解決方案。

使用之前的非透明解決方案,你可能會遇到堆疊上下文問題。使用透明解決方案,你可能會遇到邊框問題。現在你有了解決這些問題的選擇和方法。 3D轉換技巧是我最喜歡的解決方案,因為它修復了所有問題(在線生成器也會考慮它)

添加圓角

如果你嘗試在使用我們開始的非透明解決方案時向元素添加border-radius,這是一個相當簡單的任務。你只需要從主元素繼承相同的值,就完成了。

即使你沒有圓角,定義border-radius: inherit也是一個好主意。這考慮了你以後可能想要添加的任何潛在圓角或來自其他地方的圓角。

處理透明解決方案的情況就不同了。不幸的是,這意味著要找到另一個解決方案,因為clip-path無法處理曲線。這意味著我們將無法裁剪主元素內部的區域。

我們將mask屬性添加到混合中。

這部分非常繁瑣,我很難找到一個不依賴於幻數的通用解決方案。我最終得到了一個非常複雜的解決方案,它只使用一個偽元素,但是代碼是一團亂麻,只涵蓋了一些特定情況。我認為探索這條路不值得。

為了簡化代碼,我決定插入一個額外的元素。以下是標記:

<code>.box {
  position: relative;
}
.box::before {
  content: "";
  position: absolute;
  inset: -5px; /* 控制扩散 */
  transform: translate(10px, 8px); /* 控制偏移量 */
  z-index: -1; /* 将元素置于后面 */
  background: /* 你的渐变色在这里 */;
  filter: blur(10px); /* 控制模糊 */
}</code>
登入後複製
登入後複製
登入後複製
登入後複製
登入後複製

我使用自定義元素來避免與外部CSS發生任何潛在衝突。我可以使用

,但是因為它是一個常見元素,所以很容易被來自其他地方的另一個CSS規則所定位,這可能會破壞我們的代碼。第一步是定位元素並故意創建溢出:
<code>box-shadow: 10px 8px 10px 5px orange;</code>
登入後複製
登入後複製
登入後複製
登入後複製
登入後複製

代碼可能看起來有點奇怪,但我們會逐步講解其背後的邏輯。接下來,我們使用的偽元素創建漸變陰影。

<code>.box {
  position: relative;
  transform-style: preserve-3d;
}
.box::before {
  content: "";
  position: absolute;
  inset: -5px;
  transform: translate3d(10px, 8px, -1px); /* (X, Y, Z) */
  background: /* .. */;
  filter: blur(10px);
}</code>
登入後複製
登入後複製
登入後複製

正如你所看到的,偽元素使用了與所有之前的示例相同的代碼。唯一的區別是在元素上定義了3D轉換,而不是偽元素上。目前,我們有一個沒有透明度功能的漸變陰影:

請注意,元素的區域是用黑色輪廓定義的。我為什麼要這樣做?因為這樣,我就可以在其上應用一個遮罩來隱藏綠色區域內部的部分,並保留我們需要看到陰影的溢出部分。

我知道這有點棘手,但是與clip-path不同,mask屬性不會考慮元素外部的區域來顯示和隱藏內容。這就是為什麼我不得不引入額外的元素——來模擬“外部”區域。

此外,請注意,我正在使用邊框和inset的組合來定義該區域。這允許我將額外元素的padding-box保持與主元素相同,以便偽元素不需要額外的計算。

從使用額外元素中獲得的另一個有用的東西是元素是固定的,只有偽元素在移動(使用translate)。這將允許我輕鬆地定義遮罩,這是這個技巧的最後一步。

<code>.box {
  position: relative;
  z-index: 0; /* 我们强制创建一个堆叠上下文 */
}
/* 创建阴影 */
.box::before {
  content: "";
  position: absolute;
  z-index: -2;
  inset: -5px;
  transform: translate(10px, 8px);
  background: /* .. */;
  filter: blur(10px);
}
/* 复制主元素样式 */
.box::after {
  content: "";
  position: absolute;
  z-index: -1;
  inset: 0;
  /* 继承在主元素上定义的所有装饰 */
  background: inherit;
  border: inherit;
  box-shadow: inherit;
}</code>
登入後複製

完成了!我們有了漸變陰影,而且它支持border-radius!你可能期望一個複雜的面具值,裡面有很多漸變,但不是!我們只需要兩個簡單的漸變和一個mask-composite來完成魔法。

讓我們隔離元素來理解那裡發生了什麼:

<code>clip-path: polygon(-100vmax -100vmax,100vmax -100vmax,100vmax 100vmax,-100vmax 100vmax,-100vmax -100vmax,0 0,0 100%,100% 100%,100% 0,0 0)</code>
登入後複製

以下是我們得到的結果:

請注意內部半徑如何與主元素的border-radius匹配。我定義了一個大的邊框(150px)和一個等於大邊框主元素半徑的border-radius。在外部,我有一個等於150px R的半徑。在內部,我有150px R - 150px = R。

我們必須隱藏內部(藍色)部分,並確保邊框(紅色)部分仍然可見。為此,我定義了兩個遮罩層——一個只覆蓋content-box區域,另一個覆蓋border-box區域(默認值)。然後我將一個從另一個中排除以顯示邊框。

<code>.box {
  position: relative;
}
.box::before {
  content: "";
  position: absolute;
  inset: -5px; /* 控制扩散 */
  transform: translate(10px, 8px); /* 控制偏移量 */
  z-index: -1; /* 将元素置于后面 */
  background: /* 你的渐变色在这里 */;
  filter: blur(10px); /* 控制模糊 */
}</code>
登入後複製
登入後複製
登入後複製
登入後複製
登入後複製

我使用相同的技術來創建支持漸變和border-radius的邊框。 Ana Tudor也有一篇關於遮罩複合的好文章,我邀請你閱讀。

這種方法有什麼缺點嗎?

是的,這絕對不完美。你可能遇到的第一個問題與在主元素上使用邊框有關。如果你沒有考慮它,這可能會導致半徑出現輕微錯位。我們的示例中存在此問題,但你可能很難注意到它。

修復起來相對容易:為元素的inset添加邊框寬度。

<code>box-shadow: 10px 8px 10px 5px orange;</code>
登入後複製
登入後複製
登入後複製
登入後複製
登入後複製

另一個缺點是我們用於邊框的大值(示例中為150px)。此值應該足夠大以包含陰影,但不能太大以避免溢出和滾動條問題。幸運的是,在線生成器將考慮所有參數來計算最佳值。

我意識到的最後一個缺點是當你使用複雜的border-radius時。例如,如果你想對每個角應用不同的半徑,你必須為每一側定義一個變量。這並不是真正的缺點,我認為,但這可能會使你的代碼維護起來有點困難。

<code>.box {
  position: relative;
  transform-style: preserve-3d;
}
.box::before {
  content: "";
  position: absolute;
  inset: -5px;
  transform: translate3d(10px, 8px, -1px); /* (X, Y, Z) */
  background: /* .. */;
  filter: blur(10px);
}</code>
登入後複製
登入後複製
登入後複製

為了簡單起見,在線生成器只考慮統一的半徑,但你現在知道如果要考慮複雜的半徑配置如何修改代碼了。

總結

我們已經到達了終點!漸變陰影背後的魔法不再是謎團。我試圖涵蓋所有可能性以及你可能遇到的任何問題。如果我遺漏了什麼或者你發現了任何問題,請隨時在評論區報告,我會檢查一下。

同樣,考慮到事實上的解決方案將涵蓋你的大多數用例,這其中很多可能都是多餘的。然而,了解技巧背後的“為什麼”和“如何”,以及如何克服其局限性是很好的。此外,我們在玩CSS剪輯和遮罩方面得到了很好的練習。

當然,你隨時可以使用在線生成器來避免麻煩。

以上是獲得CSS梯度陰影的不同方法的詳細內容。更多資訊請關注PHP中文網其他相關文章!

本網站聲明
本文內容由網友自願投稿,版權歸原作者所有。本站不承擔相應的法律責任。如發現涉嫌抄襲或侵權的內容,請聯絡admin@php.cn
作者最新文章
熱門教學
更多>
最新下載
更多>
網站特效
網站源碼
網站素材
前端模板