現代 CSS 解決方案:全尺寸的帶圓角的漸變邊框

在之前,我們有一篇介紹帶圓角的漸變邊框的純 CSS 實現的文章:

會有這麼一個話題的本質在於,在過往,想使用純 CSS 實現純粹的,內部透明漸變邊框,是一件非常困難的事情,像是這樣:

這個效果的幾個核心難點:

  1. 邊框帶漸變色

  2. 邊框支持設置 border-radius

  3. 內部支持透明

思考一下,使用 CSS,我們可以如何實現這個效果?

過往比較好的方法

之前有一個比較接近上面的訴求的方法。主要利用了 clip-pathborder-image

clip-path[2],大家應該都非常熟悉了。它可以創建一個只有元素的部分區域可以顯示的剪切區域。區域內的部分顯示,區域外的隱藏。剪切區域是被引用內嵌的 URL 定義的路徑或者外部 SVG 的路徑。

簡而言之,這裏我們只需要在 border-image 的基礎上,再利用 clip-path 裁剪出一個帶圓角的矩形容器即可:

<div class="border-image-clip-path"></div>
.border-image-clip-path {
    position: relative;
    width: 200px;
    height: 100px;
    border: 10px solid;
    border-image: linear-gradient(45deg, gold, deeppink) 1;
    clip-path: inset(0 round 10px);
}

解釋一下:clip-path: inset(0 round 10px)

效果如下:

但是,可以看到上圖的 border-radius 的值比較大,整個邊框的寬度比較粗。

如果我們想得到一條 1px 寬度的漸變邊框,我們嘗試將上面的邊框 CSS 樣式修改一下:

border: 10px solid --> border: 1px solid

得到如下效果:

由於圓角的原因,利用了 clip-path: inset(0 round 10px) 對圖形進行切割後,元素的四個圓角都被切割掉了!

所以,有沒有一種更好的方式,實現帶圓角的漸變邊框呢?

使用 mask 和 background-clip 巧妙實現帶圓角的漸變邊框

這裏,我們介紹一種更爲巧妙的方法。主要會利用 background-clipmask 兩個核心屬性。

首先,我們利用背景 background 實現一個普通的漸變背景:

<div></div>
div {
    position: relative;
    width: 140px;
    height: 80px;
    border-radius: 100px; 
    background: conic-gradient(#ff00fa, #fe3, #0f3, #ff00fa);
}

利用角向漸變 background: conic-gradient(#ff00fa, #fe3, #0f3, #ff00fa),我們得到了這麼一個圖形:

思考一下,如果我們有辦法將圖形中間部分鏤空裁剪,我們不就能得到一個帶圓角的漸變邊框了嗎?

通過一個示意圖,你能很快明白到底是什麼意思:

好,那剩下的問題就轉換爲了:

  1. 如何裁剪掉一個元素內部的區域,並且能夠控制裁剪區域的大小

  2. 裁剪區域,與圖形的輪廓是一致的

在 CSS 中想使用裁剪功能,首先想到的肯定是 clip-path,但是上面的例子已經證明了 clip-path 無法實現細邊框的裁剪,因此,我們需要另尋解法。

而 CSS 中,另外一個與裁剪功能相關的屬性就是 mask

不瞭解 mask 的,可以戳我的這幾篇文章看看:奇妙的 CSS MASK[3]、高階切圖技巧!基於單張圖片的任意顏色轉換 [4]

在此處,我們利用 mask,並且,最爲核心的是,需要配合 mask-composite,實現圖形輪廓的精確裁剪

深入理解 mask-composite

什麼是 mask-composite

mask-composite[5]: 屬性指定了將應用於同一元素的多個蒙版圖像相互合成的方式。

通俗點來說,他的作用就是,當一個元素存在多重 mask 時,我們就可以運用 -webkit-mask-composite 進行效果疊加。

舉個栗子:

<div class="original"></div>
.original {
    background: #000;
    mask: radial-gradient(circle at 0 0, #000, #000 200px, transparent 200px);
}

我們用一個 radial-gradient 作爲 mask,切割原本的矩形,得到一個新的圖形。

如果再換一個方向:

<div class="original"></div>
.original {
    background: #000;
    mask: radial-gradient(circle at 100% 0, #000, #000 200px, transparent 200px);
}

如果我想得到這樣一個效果:

該怎麼做呢?

我們嘗試合併上述兩個 mask 的效果:

.mask {
    background: #000;
    mask: radial-gradient(circle at 100% 0, #000, #000 200px, transparent 200px),
        radial-gradient(circle at 0 0, #000, #000 200px, transparent 200px);
}

效果如下:

與我們想象的不太一樣,這是因爲,兩個 mask 的圖形疊加,就是上述圖形的效果,所以上述效果是沒有問題的。

只是,我們想得到的是兩個 mask 圖形的重疊部分:

這時,我們就可以使用 mask-composite

.mask {
    background: #000;
    mask: radial-gradient(circle at 100% 0, #000, #000 200px, transparent 200px),
        radial-gradient(circle at 0 0, #000, #000 200px, transparent 200px);
    -webkit-mask-composite: source-in;
}

添加了 -webkit-mask-composite: source-in 後,我們就可以得到兩個 mask 圖形的重疊部分,再基於這個重疊部分作用到整個 mask 遮罩:

CodePen Demo -- mask-composite Demo[6]

-webkit-mask-composite 還可以實現非常多不同的功能,包括但不限於:

-webkit-mask-composite: clear; /*清除,不顯示任何遮罩*/
-webkit-mask-composite: copy; /*只顯示上方遮罩,不顯示下方遮罩*/
-webkit-mask-composite: source-over; 
-webkit-mask-composite: source-in; /*只顯示重合的地方*/
-webkit-mask-composite: source-out; /*只顯示上方遮罩,重合的地方不顯示*/
-webkit-mask-composite: source-atop;
-webkit-mask-composite: destination-over;
-webkit-mask-composite: destination-in; /*只顯示重合的地方*/
-webkit-mask-composite: destination-out;/*只顯示下方遮罩,重合的地方不顯示*/
-webkit-mask-composite: destination-atop;
-webkit-mask-composite: xor; /*只顯示不重合的地方*/

看看這張圖,就一目瞭然(圖片源自 CSS mask 實現鼠標跟隨鏤空效果 [7])

理解 background-clip

要實現最終效果,還有一個有意思的細節點需要掌握。那就是理解 backgrund-clip

一般我們用 backgrund-clip 比較多的場景是 background-clip: text,用於將背景圖作用與文字之上。

但是,其實 backgrund-clip 還有幾個與 box-content 類似的取值:

{
  background-clip: border-box;  // 背景延伸到邊框外沿(但是在邊框之下)
  background-clip: padding-box; // 邊框下面沒有背景,即背景延伸到內邊距外沿。
  background-clip: content-box; // 背景裁剪到內容區 (content-box) 外沿。
}

什麼意思呢?background-clip 設置元素的背景(背景圖片或顏色)是否延伸到邊框下面。看看下面這張圖(圖片源自:CSS Background Clip[8]),就是對 background-clip 很好的一個闡述:

而本文,我們會用到 content-box,舉個例子:

<div></div>
div {
  width: 140px;
  height: 80px;
  border-radius: 100px; 
  border: 1px dashed #000;
  background: conic-gradient(#ff00fa, #fe3, #0f3, #ff00fa);
  background-clip: content-box;
  padding: 10px; 
}

效果如下:

可以看到,此時,背景的填充不再是從元素的右上角開始,而是在內容區域,算上了 10px 的 padding 之後,開始繪製。

也就是說,基於 background-clip,是可以改變元素背景的繪製規則!這一點非常重要。

利用 mask 配合 mask-composite 實現圖形輪廓裁剪

只有在掌握了 mask-compositebackground-clip 的基礎上,你才能理解下面整個裁剪代碼個核心精髓處。

基於上述講解,我們就可以利用上面的綜合技巧,實現我們最終想要的 -- 帶圓角的漸變邊框

代碼如下:

<div></div>
div {
position: relative;
    width: 140px;
    height: 80px;
    border-radius: 100px; 
    background: conic-gradient(#ff00fa, #fe3, #0f3, #ff00fa);
    padding: 1px;
    -webkit-mask: 
        linear-gradient(#fff 0 100%) content-box,
        linear-gradient(#fff 0 100%);
    -webkit-mask-composite: xor;
}

這樣,我們就完美的得到了一個 1px 寬度的帶圓角的漸變邊框,並且,內部是鏤空的

通過控制上述的 padding: 1px 來控制元素的邊框寬度。

完整的代碼,你可以戳這裏查看:CodePen Demo -- 純 CSS 實現帶圓角的漸變邊框![9]

最後

好了,本文到此結束,希望本文對你有所幫助 :)

如果還有什麼疑問或者建議,可以多多交流,原創文章,文筆有限,才疏學淺,文中若有不正之處,萬望告知。

參考資料

[1]

巧妙實現帶圓角的漸變邊框: https://github.com/chokcoco/iCSS/issues/77

[2]

clip-path: https://developer.mozilla.org/zh-CN/docs/Web/CSS/clip-path

[3]

奇妙的 CSS MASK: https://github.com/chokcoco/iCSS/issues/80

[4]

高階切圖技巧!基於單張圖片的任意顏色轉換: https://github.com/chokcoco/iCSS/issues/189

[5]

mask-composite: https://developer.mozilla.org/en-US/docs/Web/CSS/-webkit-mask-composite

[6]

CodePen Demo -- mask-composite Demo: https://codepen.io/Chokcoco/pen/KKQjxMP

[7]

CSS mask 實現鼠標跟隨鏤空效果: https://segmentfault.com/a/1190000040996523

[8]

CSS Background Clip: https://www.programiz.com/css/background-clip

[9]

CodePen Demo -- 純 CSS 實現帶圓角的漸變邊框!: https://codepen.io/Chokcoco/pen/JjqJqZR

本文由 Readfog 進行 AMP 轉碼,版權歸原作者所有。
來源https://mp.weixin.qq.com/s/2g79GVEGtQAqBwaSNprZLQ