我们最近介绍了用CSSmask 属性创建花哨的边框,现在我们要用CSSmask 和 clip-path 来切割边角了!有很多技术可以从任何元素的边角处切割出不同的形状。在这篇文章中,我们将考虑使用现代技术来创建独特的边角形状,同时尝试使用可重复使用的代码,使我们能够通过调整变量产生不同的结果。
看看这个在线工具,以了解我们正在建造的东西。这是一个CSS生成器,你可以选择形状、角和尺寸,然后你就可以马上得到代码了
我们主要有两种类型的切割:一种是圆形的,一种是有角度的。对于每一种,我们都可以得到完整的形状或仅有边界的形状,更不用说我们可以选择我们想要切割的角。很多的组合!
和上一篇文章一样,我们将大量使用CSS的 mask属性。所以,如果你对它不熟悉,我建议在继续之前先读一下我写的快速入门指南。
圆形剪裁
对于圆形或圆形的切割,我们将使用radial-gradient() 。为了切割四个角,合理的解决方案是创建四个梯度,每个角一个。
每个渐变都要占用元素的四分之一的尺寸。梯度的语法是不言自明的。
radial-gradient(circle 30px at top left, #0000 98%, red) top left;
翻译一下,这在左上角渲染了一个半径为30px 的圆。主色是透明的(#0000),其余的是red 。整个渐变也被放置在元素的左上角,使其开始于元素的左上角。其他三个渐变的逻辑相同。关键字circle 可以省略,因为我们明确指定了一个半径值。
就像我在上一篇文章中做的那样,这次我将使用稍大或稍小的值,以避免不良的视觉效果。在这里,我使用98% ,而不是100% ,以避免锯齿状的边缘,使用51% ,而不是50% ,以创造梯度之间的重叠,避免白色空间。这个逻辑将贯穿本文。事实上,你会发现,添加或删除1% 或1deg 通常会产生一个漂亮的视觉效果。
我们将此应用于CSSmask 属性,我们就完成了!
我们实际上可以对该代码进行一些优化。
--g: #0000 98%,#000;
--r: 30px;
mask:
radial-gradient(var(--r) at 0 0 ,var(--g)) 0 0,
radial-gradient(var(--r) at 100% 0 ,var(--g)) 100% 0,
radial-gradient(var(--r) at 0 100%,var(--g)) 0 100%,
radial-gradient(var(--r) at 100% 100%,var(--g)) 100% 100%;
mask-size: 51% 51%;
mask-repeat: no-repeat;
这样,我们使用自定义属性来处理多余的值,而且,作为个人偏好,我使用数值来代替关键词的位置。
在生成器中,我将使用以下语法。
--g: #0000 98%,#000;
--r: 30px;
mask:
radial-gradient(var(--r) at 0 0 ,var(--g)) 0 0 /51% 51% no-repeat,
radial-gradient(var(--r) at 100% 0 ,var(--g)) 100% 0 /51% 51% no-repeat,
radial-gradient(var(--r) at 0 100%,var(--g)) 0 100%/51% 51% no-repeat,
radial-gradient(var(--r) at 100% 100%,var(--g)) 100% 100%/51% 51% no-repeat;
这种速记语法更容易生成,加上整个值可以作为一个自定义属性使用。
如果我们想的话,我们可以使用更少的梯度吗?
当然可以!一个梯度就可以完成工作。
在这里,我们定义了一个没有大小的radial-gradient() (默认是100% 高度和100% 宽度)。这让我们在中心有一个洞。我们将梯度平移/移动图片宽度和高度的一半,将洞移到一个角落。由于默认情况下,CSS遮罩是重复的,所以我们在每个角落都得到了相同的效果。我们只用一个渐变就有了四个切角!
这个方法的唯一缺点是,我们需要事先知道元素的宽度和高度。
难道我们不能用-50% ,而不是用一半的宽度和高度吗?
不幸的是,我们在这里无法做到这一点,因为百分比在与CSSmask-position 属性一起使用时,其行为与像素值不尽相同。它们是很棘手的。
我有一个详细的Stack Overflow答案,解释了其中的区别。它涉及到background-position ,但同样的逻辑也适用于CSSmask-position 属性。
然而,我们可以使用一些技巧来使它与百分比值一起工作,而且不需要知道宽度或高度。当梯度(或背景层)的宽度和高度与元素相等时,我们不能用百分比值来移动它。所以我们需要改变它的大小
我将定义一个等于99.5% 99.5% 的大小。我将从宽度和高度上减少0.5% ,使其具有与100% 不同的值,同时保持相同的视觉效果,因为我们不会注意到100% 和99.5% 之间的巨大差异。现在,我们的梯度有一个与100% 不同的尺寸,我们可以用百分比值来移动它。
我不会详述所有的数学运算,但要移动它的宽度和高度的一半,我们需要使用这个公式。
100% * (50/(100 - 99.5)) = 100% * 100 = 10000%
这是一个奇怪的数值,但它能完成工作。
正如你所看到的,这一招效果很好。无论元素的大小如何,我们都可以只用一个梯度来切割四个角。然而,当元素的宽度或高度为小数时,这种方法有一个小缺点。这里有一个例子,图像的宽度等于150.5px 。
使用99.5% ,结合150.5px ,会产生四舍五入的问题,会破坏计算,导致掩码错位。所以,要谨慎使用这种方法。
为了克服四舍五入问题,我们可以将最后一招与一个伪元素结合起来。
以下是里面的内容。
- 我们定义一个伪元素,作为我们的背景层。从逻辑上讲,我们应该使用
inset:0,使其覆盖整个区域,但我们将通过使用inset: -10%,创建一个小的溢出,这意味着伪元素将溢出每边10%。 - 我们将我们的CSS掩码设置到伪元素上。遮罩的大小需要与主元素的大小相匹配,而不是与伪元素相匹配。换句话说,它将小于伪元素的大小,这就是我们希望能够使用百分比值来移动的东西。在我们进行计算后,大小需要
100%/1.2。注意在上面的演示中,CSS掩码在绿色边框内,这样它就与容器的大小相匹配。 - 现在,我们需要以模拟切割主元素角落的方式来移动它。孔的中心需要在主元素的角上,如演示中所示。为了做到这一点,我们使用
mask-position: 300% 300%(300% = 50%/(1 - 1/1.2))。 - 我们删除
no-repeat,以激活重复,并在每个角落获得相同的效果。 - 我们剪掉溢出的部分,就得到了我们的最终结果
我知道这有点矫枉过正,但它确实有效,而且它只需要一个梯度而不是四个。
让我们快速回顾一下刚才的三种方法。
- 第一种方法使用四个梯度,就使用情况而言,没有任何缺点。当然,它很冗长,但它适用于任何类型的元素和尺寸。我推荐使用这个方法。
- 第二种方法使用一个梯度,适用于任何元素,但在某些特殊情况下会出现问题。它适用于固定尺寸的元素。它可以使用,但可能不太频繁。
- 第三种方法使用一个梯度,需要一个伪元素。它不能用于
<img>和其他不能支持伪元素的元素。
生成器只支持第一和第三种方法。
现在我们看到了所有角的情况,让我们禁用其中一些角。使用第一种方法,任何我们想保留的角都不需要切割,我们只需移除其渐变,并调整剩下的大小。
要禁用右上角。
- 我们删除右上角的渐变(蓝色的那个)。
- 我们有一个空的角落,所以我们增加红色渐变的大小(或紫色的),以覆盖剩余的空间。
完成了!
你可能看到我们在这里可以做多少种可能性和组合。如果我们想削减N 的角落(其中N 从1 到4 ),我们使用N 梯度。我们所需要的是正确设置每一个的大小,不留任何空间。
那其他只有一个梯度的方法呢?我们将需要另一个梯度!那两种方法只用一个radial-gradient() 来切角,所以我们将依靠另一个梯度来 "隐藏 "切角。我们可以使用一个有四个部分的conic-gradient() 来完成这个任务。
conic-gradient(red 25%, blue 0 50%, green 0 75%, purple 0)
我们把它加在径向渐变的顶部,得到以下结果。
conic-gradient() 覆盖了radial-gradient() ,没有角落被切割。让我们把conic-gradient() 中的一种颜色改为透明。例如,右上方的那个。
你看到了吗?我们揭示了radial-gradient() 的一个角,并且我们以一个切角结束!
仅有边框的圆形剪裁
让我们来制作前一个形状的纯边框版本。换句话说,我们实现了相同的形状,但打掉了填充物,所以我们只剩下了形状的边框。
这是一个有点棘手的问题,因为我们有不同的情况,有不同的代码。公平的警告,我将在这里使用大量的梯度,同时寻找机会来修饰它们的数量。
需要注意的是,在这种情况下,我们将考虑一个伪元素。只显示边界意味着我们需要隐藏形状的内部 "填充"。将此应用于主元素也将隐藏内容--这就是为什么这是一个伪元素的好用例。
一个切角
这个需要一个径向梯度和两个圆锥梯度。
第一个例子说明了径向渐变(红色)和两个圆锥渐变(蓝色和绿色)。在第二个例子中,我们将它们全部应用在CSSmask 属性内,以创建只有一个切角的边界形状。
下面是游戏计划的图示。
如图所示,radial-gradient() ,创建一个圆的四分之一,每个conic-gradient() ,创建两个垂直线段来覆盖两个边。需要注意的是,渐变的重叠不是一个问题,因为我们不打算改变CSS [mask-composite](https://css-tricks.com/almanac/properties/m/mask-composite/)属性的值。
使用同样的代码并调整一些变量,我们可以得到其他角的形状。
两个切角
对于两个角的配置,我们有两种情况发生。
在第一种情况下,有两个相对的角,我们需要两个径向梯度和两个圆锥梯度。
这种配置几乎与只切割一个角相同:我们添加一个额外的梯度并更新一些变量。
在第二种情况下,有两个相邻的角,在这种情况下,我们需要两个径向梯度、一个圆锥梯度和一个线性梯度。
"等等!"你可能会感叹。"圆锥渐变怎么会覆盖三个边?"如果你检查代码,注意到repeat-y 。在所有的例子中,我们总是用no-repeat 来做渐变,但是对于这个,我们可以重复其中的一个来覆盖更多的面,减少我们使用的渐变的数量。
下面是一个只有conic-gradient() 的例子,以了解重复的情况。诀窍是高度等于100% 减去边框的大小,这样在重复时,渐变就会填满这个空间,在这个过程中覆盖第三面。
三个切角
对于这个配置,我们需要三个径向渐变,一个圆锥渐变,和两个线性渐变。
CodePen Embed Fallback
四个切角
需要四个径向渐变和两个线性渐变来切割所有四个角。
我可以听到你的尖叫声,"我怎么能记住所有这些情况呢!"你不需要记住任何东西,因为你可以使用在线生成器轻松生成每个案例的代码。你所需要的是理解整体的技巧,而不是每个单独的案例。这就是为什么我只对第一个配置进行了详细说明--其余的只是对技巧的最初基础进行调整的迭代。
请注意,在这些例子中,我们一直遵循一个一般的模式。
- 我们在我们想要切割的角上添加一个
radial-gradient()。 - 我们用
conic-gradient()或linear-gradient()来填补边上的空白,从而形成最终的形状。
应该注意的是,我们可以找到不同的方法来创建相同的形状。我在这篇文章中所展示的是我在尝试了许多其他想法后发现的最佳方法。你可能有一个你认为更好的不同方法!如果是这样,一定要在论坛上分享。如果是这样的话,一定要在评论中分享!
有角度的剪裁
让我们来解决另一种类型的切割形状:有角度的切割。
我们有两个参数:切割的大小和角度。为了得到这个形状,我们需要在每个角上有一个conic-gradient() 。这种配置与本文开篇的例子非常相似。
这里有一个角的插图,以了解这个技巧。
每个角之间的区别是在from 和at 位置的额外偏移90deg 。完整的代码就像下面这样。
--size: 30px;
--angle: 130deg;
--g: #0000 var(--angle), #000 0;
mask:
conic-gradient(from calc(var(--angle)/-2 - 45deg)
at top var(--size) left var(--size),var(--g)) top left,
conic-gradient(from calc(var(--angle)/-2 + 45deg)
at top var(--size) right var(--size),var(--g)) top right,
conic-gradient(from calc(var(--angle)/-2 - 135deg)
at bottom var(--size) left var(--size),var(--g)) bottom left,
conic-gradient(from calc(var(--angle)/-2 + 135deg)
at bottom var(--size) right var(--size),var(--g)) bottom right;
mask-size: 51% 51%;
mask-repeat: no-repeat;
如果我们想禁用一个角,我们删除该角的conic-gradient() ,并更新另一个角的大小,以填补剩余的空间,就像我们对圆形切割所做的那样。
我们可以对所有其他的角做完全相同的事情,以获得同样的效果。
除了CSS遮罩之外,我们还可以使用CSS clip-path属性来切角。每个角可以用三个点来定义。
该形状由切割两端的两个点组成,它们之间有一个点形成角度。
其他的角将有相同的值,偏移量为100% 。这使我们最终的代码中共有12个点--每个角三个。
/* I will define T = [1-tan((angle-90)/2)]*size */
clip-path: polygon(
/* Top-left corner */
0 T, size size,0 T, /* OR 0 0 */
/* Top-right corner */
calc(100% - T) 0,calc(100% - size) size,100% T, /* OR 100% 0 */
/* Bottom-right corner*/
100% calc(100% - T),calc(100% - size) calc(100% - size), calc(100% - T) 100%, /* OR 100% 100% */
/* Bottom-left corner */
T 100%, size calc(100% - size),0 calc(100% - T) /* OR 0 100% */
)
注意该代码中的OR 注释。它定义了如果我们想禁用某个角,我们必须考虑的代码。要切割一个角,我们使用三个点。要取消一个角,我们使用一个点--这只不过是该角的坐标。
仅限边框的斜切
哦,我们终于到了最后一个最棘手的形状了!这个形状可以用渐变的方式实现。这个可以用梯度或clip-path ,但我们还是用clip-path 。
如果我们采用渐变的方法,事情会变得复杂而冗长。
一共有九个梯度,我还没有完成计算。正如你所知道的,边界的厚度是不正确的,再加上梯度的性质和其抗锯齿问题,最终的结果是不令人满意的。这种方法可能是一个很好的练习,可以挑战梯度的极限,但我不建议在生产环境中使用。
所以,回到clip-path 方法。我们仍然会产生冗长的代码,但这并不是什么大问题,因为生成器可以为我们完成这项工作,而且最终的结果更干净。
这里是路径的概述。为了更好地看清不同的点,我加了一个小缺口,但我们应该有一个重叠的点。
我们有13个外部点(黑色的)和13个内部点(蓝色的)。
我们计算外侧点的方法与我们计算常规角度切割的方法相同。然而,对于内点,我们需要更多的数学计算。别担心,我不会给你一些 "无聊 "的几何学解释。我知道你们中的大多数人都不希望这样,但如果你们需要深入了解,你们可以查看生成器的JavaScript文件,找到我用来生成形状的代码和数学知识。
180deg 的特殊情况
在我们结束之前,我想说的是角度切割的一个特殊情况。这就是我们使用一个等于180deg 的角度。下面是产生的情况。
我们在角上有一条直线,所以我们可以优化clip-path 代码。对于完整的形状,我们可以使用8个点(每个角两个点)而不是12个。而对于仅有边界的版本,我们可以使用18个点(9个内点和外点),而不是26个。换句话说,我们可以删除中间的点。
仅有边框的形状也可以使用渐变来制作。但是,与其像我们之前那样使用九个渐变,我们可以只用四个线性渐变,就可以得到一个干净的结果。
CodePen嵌入回退
总结
我们刚刚将 CSS 掩码与渐变结合起来,创造了一些花哨的形状,而不需要借助黑客和大量的代码!我们还体验到了需要花费多少时间。我们还体验了如何在代码中取得适当的平衡以获得正确的结果。我们甚至还学到了一些小技巧,比如改变一个甚至半个单位的数值。CSS是超级强大的!
但是,正如我们所讨论的,我制作的在线生成器是一个获得你所需要的代码的好地方,而不是手工写出来。我的意思是,我经历了所有的工作,弄清楚这一切是如何运作的,而我很可能仍然需要参考这篇文章来记住这一切是如何组合的。如果你能记住这一切,那就赞一个!但有一个好的参考资料也很好。但是,有一个生成器可以依靠也是不错的。