使用掩码制作花式CSS边框的方法

337 阅读11分钟

你有没有试过把CSS的边框做成重复的之字形?比如在网站的一个彩色部分结束,另一个不同颜色部分开始的地方--不是用直线,而是有角度的之字形、圆形的驼峰或波浪。有很多方法可以实现这种CSS边框,可以一直追溯到使用background-image 。但我们可以用它来做得更现代、更程式化。在这篇文章中,我们将看看一些现代的CSS遮罩技术来实现这种外观。

不过,在我们深入研究技术部分之前,让我们先看看我们正在构建的东西。我做了一个CSS边框生成器,你可以在几秒钟内轻松生成任何种类的边框,并获得CSS代码。

你看到了吗?通过CSSmask 属性和一些CSS渐变,我们得到了一个反应灵敏、外观很酷的边框--所有这些都是通过CSS本身完成的。不仅如此,这种效果还可以应用于任何可以有任何颜色的元素(如图像、渐变等)。我们可以得到这一切,而不需要额外的元素、伪元素或不知从何而来的神奇数字

哦,太好了!我所要做的就是复制这些元素。我所要做的就是复制/粘贴代码,然后就完成了!

是的,但了解逻辑是很好的,如果你需要,可以手动调整代码。

遮蔽东西

由于我们所有的效果都依赖于CSSmask 属性,让我们快速回顾一下它是如何工作的。直接来自规范

对图形对象应用遮罩的效果,就好像图形对象将通过遮罩被涂到背景上,从而完全或部分地遮住了图形对象的一部分。

如果我们检查mask 属性的正式语法,我们可以看到它接受一个<image> 作为值,这意味着要么是图像的URL,要么是颜色渐变。梯度是我们将在这里使用的。让我们从基本的例子开始。

在这个演示的第一个例子中,一个渐变被用来使它看起来好像图像正在逐渐消失。同时,第二个例子也使用了渐变,但不是颜色之间的柔和过渡,而是用一个硬的色块来隐藏(或掩盖)图像的一半。第二个例子说明了我们将使用的技术来创建我们的花式边框。

哦,CSSmask 属性可以接受多个梯度,只要它们是以逗号分隔的。这意味着我们有更多的控制权来掩盖图像的其他部分。

那个显示多个遮蔽渐变的例子乍一看可能有点棘手,但所发生的事情与应用background 属性上的多个渐变是一样的。但我们不是使用与页面背景相融合的颜色,而是对图像的隐藏部分使用一个 "透明 "的黑色值 (#0000) ,对可见部分使用全黑 (#000) 。

就这样了!现在我们可以处理我们的花式边框了。

之字形的CSS边框

正如我们在本文开头的视频中所看到的,生成器可以在一边、两边或所有边上应用边框。让我们从底面开始,使用一步步的图示。

  1. 我们首先在顶部添加第一个带有纯色的渐变层(red)。用一个等于calc(100% - 40px) 的高度,在底部留下40px 的空位。
  2. 我们在底部添加第二个渐变层,它占用了容器的剩余高度。为了实现这个目标,有一点几何学上的变化。

Diagram showing how the shape of a zig-zag is created in CSS. An upside down triangle in blue represents the shape and green areas to the left and right of it show the leftover space that is masked out with CSS.

  1. 接下来,我们在水平方向上重复最后一个梯度(用repeat-x 替换no-repeat )。我们已经可以看到 "之 "字形了。
  2. 众所周知,梯度有抗锯齿问题,会产生锯齿状的边缘(尤其是在Chrome上)。为了避免这种情况,我们在颜色之间添加一个轻微的过渡,将blue 90deg, green 0 改为green, blue 1deg 89deg, green 90deg
  3. 然后我们更新颜色,使其具有统一的形状
  4. 最后,我们使用mask 属性内的所有内容!

我们可以从这些步骤中提取两个变量来定义我们的形状:大小 (40px) 和角度 (90deg)。下面是我们如何使用这些变量的占位符来表达。我将使用JavaScript将这些变量替换成它们的最终值。

mask:
  linear-gradient(red 0 0) top/100% calc(100% - {size}) no-repeat,
  conic-gradient(
    from {-angle/2} at bottom,
    #0000, #000 1deg {angle - 1} ,#0000 {angle}
  ) bottom/{size*2*tan(angle/2)} {size} repeat-x;

我们可以为大小和角度使用CSS自定义属性,但三角函数目前是不支持的功能。在未来,我们将能够做这样的事情。

--size: 40px;
--angle: 90deg;
mask:
  linear-gradient(red 0 0) top/100% calc(100% - var(--size)) no-repeat,
  conic-gradient(
    from calc(var(--angle)/-2) at bottom,
    #0000, #000 1deg calc(var(--angle) - 1deg), #0000 var(--angle)
  ) bottom/calc(var(--size)*2*tan(var(--angle)/2)) var(--size) repeat-x;

与底部边框类似,顶部的边框也会有几乎相同的代码,只是做一些调整。

mask:
  linear-gradient(red 0 0) bottom/100% calc(100% - {size}) no-repeat,
  conic-gradient(
    from {180deg - angle/2} at top,
    #0000, #000 1deg {angle - 1}, #0000 {angle}
  ) top/{size*2*tan(angle/2)} {size} repeat-x;

我们把bottom 改为top ,把top 改为bottom ,然后把梯度的旋转更新为180deg - angle/2 ,而不是-angle/2 。 就这么简单!

这就是我们可以用在其他侧面的模式,比如左边。

mask:
  linear-gradient(red 0 0) right/calc(100% - {size}) 100% no-repeat,
  conic-gradient(
    from {90deg - angle/2} at left,
    #0000, #000 1deg {angle - 1}, #0000 {angle}
  ) left/{size} {size*2*tan(angle/2)} repeat-y;

...和右边。

mask:
  linear-gradient(red 0 0) left/calc(100% - {size}) 100% no-repeat,
  conic-gradient(
    from {-90deg - angle/2} at right,
    #0000, #000 1deg {angle - 1}, #0000 {angle}
  ) right/{size} {size*2*tan(angle/2)} repeat-y;

让我们为它们同时应用于两边的时候做边框。我们实际上可以重复使用相同的代码。为了得到顶部和底部的边框,我们简单地结合顶部和底部边框的代码。

我们使用顶部的conic-gradient() ,底部的conic-gradient() ,再加上一个linear-gradient() 来覆盖中间区域。

mask:
  linear-gradient(#000 0 0) center/100% calc(100% - {2*size}) no-repeat,
  conic-gradient(
    from {-angle/2} at bottom,
    #0000, #000 1deg {angle - 1},
    #0000 {angle}
  ) bottom/{size*2*tan(angle/2)} {size} repeat-x;
  conic-gradient(
    from {180deg - angle/2} at top, 
    #0000, #000 1deg {angle - 1}, #0000 {angle}
  ) top/{size*2*tan(angle/2)} {size} repeat-x;

在左右两边一起应用边框时也是如此。

mask:
  linear-gradient(#000 0 0) center/calc(100% - {2*size}) 100% no-repeat,
  conic-gradient(
    from {90deg - angle/2} at left,
    #0000, #000 1deg {angle - 1}, #0000 {angle}
  ) left/{size} {size*2*tan(angle/2)} repeat-y,
  conic-gradient(
    from {-90deg - angle/2} at right,
    #0000, #000 1deg {angle - 1}, #0000 {angle}
  ) right/{size} {size*2*tan(angle/2)} repeat-y;

CodePen Embed Fallback

所以,如果我们想一次将边框应用到所有的侧面,我们将所有的梯度加在一起,对吗?

没错!我们有四个圆锥梯度(即 "L")。我们有四个锥形渐变(每边一个)和一个linear-gradient() 在中间。我们设置了一个等于90deg 的固定角度,因为只有这个角度可以产生更漂亮的角,而不会出现奇怪的重叠。请注意,我也使用了space ,而不是repeat-xrepeat-y ,以避免像这样的角落的坏结果。

调整一个四边配置的容器的大小

圆形的CSS边框

现在让我们来解决圆角边框的问题!

哦,不!又是一个有很多计算的长篇解释?

一点也不!这里没有什么可解释的。conic-gradient() 我们从 "之 "字形的例子中提取所有内容,然后用radial-gradient() 。这就更容易了,因为我们没有任何角度需要处理--只有尺寸变量。

圆形的CSS边框:

同样,我所做的只是将conic-gradient() (使用尺寸的占位符)。

background: 
  radial-gradient(circle farthest-side, #0000 98%, #000) 
  50% calc(100% + {size})/{1.85*size} {2*size} repeat-x

而这是第二个。

background:
  radial-gradient(circle farthest-side, #000 98%, #0000) 
  bottom/{1.85*size} {2*size} repeat-x

1.85和98%这两个神奇数字背后的逻辑是什么?

从逻辑上讲,我们应该使用100% ,而不是98% ,以便有一个接触到背景区域边缘的圆;但同样,这也是抗锯齿问题和那些锯齿状的边缘。我们使用一个稍小的值来防止奇怪的重叠。

1.85 的值更多的是个人的偏好,而不是什么。我最初使用的是2 ,这是获得一个完美圆圈的逻辑值,但结果看起来并不那么好,所以较小的值在圆圈之间创造了一个更无缝的重叠。

这就是区别。

现在,我们需要在其余的边上复制这个方法,就像我们对人字形CSS边框所做的那样。

然而,在一次应用所有四个边时,有一个小的区别。 你会注意到,对于其中一个圆形边框,我只用了一个radial-gradient() ,而不是四个。这是有道理的,因为我们可以用一个梯度在所有的边上重复一个圆形。

这里是最终的CSS。

mask:
  linear-gradient(#000 0 0) center/calc(100% - {1.85*size}) calc(100% - {1.85*size}) no-repeat,
  radial-gradient(farthest-side,#000 98%,#0000) 0 0/{2*size} {2*size} round;

注意我是如何使用round ,而不是repeat 。这是为了确保我们不会切断任何一个圆圈。还有,1.85 这个值是一个个人偏好的值。

对于另一种类型的圆形边框,我们仍然要使用四个径向渐变,但我不得不引入CSS clip-path属性来纠正边角处的重叠问题。

这是一个八点式的切角路径。

clip-path: polygon(
   {2*size} 0,calc(100% - {2*size}) 0,
   100% {2*size},100% calc(100% - {2*size}),
   calc(100% - {2*size}) 100%,{2*size} 100%,
   0 calc(100% - {2*size}),0 {2*size}
);

波浪形的CSS边框

人字形和圆形的CSS边框都需要一个梯度来获得我们想要的形状。那么波浪形的边框呢?这需要两个渐变。下面是一个插图,以了解我们如何用两个径向渐变创造一个波浪。

Showing three diagrams of CSS borders, each with a piece of the border and an accompanying snippet of CSS to achieve the effect.It shows how one part cuts a circular white shape out of a red rectangle. The second part showing how to create a red circle shape. The third part shows two radial gradients used to position the two circles so they combine to create the wave shape.

我们在底部重复这个形状,再加上顶部的线性渐变,我们在底部得到了波浪形的边界。

mask: 
  linear-gradient(#000 0 0) top/100% calc(100% - {2*size}) no-repeat,
  radial-gradient(circle {size} at 75% 100%,#0000 98%,#000) 50% calc(100% - {size})/{4*size} {size} repeat-x,
  radial-gradient(circle closest-side at 25% 50%,#000 99%,#0000 101%) bottom/{4*size} {2*size} repeat-x;

我们对其他边做同样的处理,就像我们对之字形和圆形的CSS边框所做的那样。我们只需要更新几个变量,就可以让每一面有不同的波浪。

显示每一面的部分CSS。 你可以在生成器上找到完整的代码。

在所有四边都应用波浪形的CSS边框呢?我们总共会有9个梯度吗?"

不是的,这是因为没有一个在所有四边都应用波浪形边框的演示。我无法找到一种梯度的组合,可以在角落里得到很好的效果。也许读到这篇文章的人知道一个好方法?

这是很有边界的好东西!

所以,你知道我这个很酷的在线CSS边框生成器的来龙去脉了!你可以使用它的代码。当然,你可以使用它吐出的代码并做得很好--但现在你有了使它发挥作用的秘方。

具体来说,我们看到了梯度是如何被用来掩盖一个元素的部分的。然后我们在多个渐变上下功夫,用这些渐变的CSS遮罩做出某些形状。其结果是一个可以沿着元素的边缘使用的图案,创造出花哨的边框,否则你可能会导致background-image 。只是这样一来,只需要交换一些数值就可以改变外观,而不是替换整个光栅图像文件或其他东西。