Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

全尺寸的带圆角的渐变边框 #266

Open
chokcoco opened this issue Jun 5, 2024 · 0 comments
Open

全尺寸的带圆角的渐变边框 #266

chokcoco opened this issue Jun 5, 2024 · 0 comments

Comments

@chokcoco
Copy link
Owner

chokcoco commented Jun 5, 2024

在之前,我们有一篇介绍带圆角的渐变边框的纯 CSS 实现的文章:

会有这么一个话题的本质在于,在过往,想使用纯 CSS 实现纯粹的,内部透明渐变边框,是一件非常困难的事情,像是这样:

这个效果的几个核心难点:

  1. 边框带渐变色
  2. 边框支持设置 border-radius
  3. 内部支持透明

思考一下,使用 CSS,我们可以如何实现这个效果?

过往比较好的方法

之前有一个比较接近上面的诉求的方法。主要利用了 clip-pathborder-image

clip-path,大家应该都非常熟悉了。它可以创建一个只有元素的部分区域可以显示的剪切区域。区域内的部分显示,区域外的隐藏。剪切区域是被引用内嵌的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)

  • clip-path: inset() 是矩形裁剪
  • inset() 的用法有多种,在这里 inset(0 round 10px) 可以理解为,实现一个父容器大小(完全贴合,垂直水平居中于父容器)且 border-radius: 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高阶切图技巧!基于单张图片的任意颜色转换

在此处,我们利用 mask,并且,最为核心的是,需要配合 mask-composite,实现图形轮廓的精确裁剪

深入理解 mask-composite

什么是 mask-composite

mask-composite: 属性指定了将应用于同一元素的多个蒙版图像相互合成的方式。

通俗点来说,他的作用就是,当一个元素存在多重 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

-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 实现鼠标跟随镂空效果

理解 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 设置元素的背景(背景图片或颜色)是否延伸到边框下面。看看下面这张图,就是对 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 实现带圆角的渐变边框!

最后

好了,本文到此结束,希望本文对你有所帮助 :)

想 Get 到最有意思的 CSS 资讯,千万不要错过我的公众号 -- iCSS前端趣闻 😄

更多精彩 CSS 技术文章汇总在我的 Github -- iCSS ,持续更新,欢迎点个 star 订阅收藏。

如果还有什么疑问或者建议,可以多多交流,原创文章,文笔有限,才疏学浅,文中若有不正之处,万望告知。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

1 participant