草庐IT

基于CSS mask和clip-path实现切角的技巧

南城FE 2023-03-28 原文
本文翻译自 Tricks to Cut Corners Using CSS Mask and Clip-Path Properties,略有删改

原作者:Temani Afif

我们最近使用CSS mask属性创建花哨的边框,本文将使用CSS maskclip-path来切元素的四个角!使用多种技巧可以从任何元素的角切割不同的形状。在本文中,我们将考虑创建独特角落形状的现代技术,同时尝试使用可重用代码,该代码允许我们仅通过调整变量即可产生不同的结果。

查看此在线工具,了解我们正在构建的内容。这是一个CSS生成器,你可以选择不同的形状、角落和大小,可实时获得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通常会带来良好的视觉效果。

接下来将此逻辑应用于CSS mask属性,效果如下: 核心CSS代码如下:

.mask { -webkit-mask: radial-gradient(circle 30px at top left ,#0000 98%,#000) top left, radial-gradient(circle 30px at top right,#0000 98%,#000) top right, radial-gradient(circle 30px at bottom left ,#0000 98%,#000) bottom left, radial-gradient(circle 30px at bottom right,#0000 98%,#000) bottom right; -webkit-mask-size:51% 51%; -webkit-mask-repeat:no-repeat; } 上面的代码有很多数值是重复的,我们可以稍微优化一下该代码:

--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; 这样我们可以对冗余值使用自定义属性,作为个人偏好,我使用位置的数字值而不是关键字。

在生成器中,将使用以下语法,省去mask-sizemask-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;

我们可以使用更少的渐变吗?

当然!一个渐变也可以完成这个效果。以下动图hover效果就是基于一个渐变实现:

在这里,我们定义了一个没有尺寸的radial-gradient(),默认情况下它是100%的高度和100%宽度。这让我们在中间有一个洞。我们将渐变平移到图像宽度和高度的一半,将孔移动到一个角落。由于默认情况下,CSS mask重复,因此我们在每个角落都得到相同的效果。我们有四个切角,只使用了一个渐变!

这种方法的唯一缺点是,我们需要提前知道元素的宽度和高度。

我们能用 -50% 而不是一半的宽度和高度吗?

不幸的是,在这里无法做到这一点,因为当与CSS mask-position属性一起使用时,百分比的行为与像素值不同,比较难处理。

我有一个详细的堆栈溢出答案来解释差异。它涉及background-position,但相同的逻辑适用于CSS mask-position属性。

然而,我们可以使用一些技巧来使其与百分比值一起工作,无需知道宽度或高度。当渐变(或背景图层)的宽度和高度等于元素时,我们无法使用百分比值移动它。所以我们需要改变它的尺寸!

定义一个等于99.5% 99.5%的尺寸。将宽度和高度降低了0.5%,此时基本保持相同的视觉结果,因为我们不会注意到100%和99.5%之间的巨大差异。现在我们的渐变尺寸与100%不同,我们可以使用百分比值移动它。

要将其移动一半的宽度和高度,我们需要使用这个方程:

100% * (50/(100 - 99.5)) = 100% * 100 = 10000% 看着是一个比较奇怪的值,但是是可以正常运行的。

mask: radial-gradient(30px,#0000 98%,#000) 10000% 10000%/99.5% 99.5% 这个技巧用的很好。无论元素的大小是多少,我们只能用一个渐变切割四个角落。但是当元素的宽度或高度是小数值时,这种方法有一个很小的缺点。以下是一个宽度等于150.5px的图像示例: 使用99.5%和150.5px将产生四舍五入问题,从而破坏计算,导致mask有一定的错位。因此,请谨慎使用这种方法。

有一个只有一个渐变的解决方案,没有四舍五入问题。使用以下代码:

mask: radial-gradient(30px at 30px 30px,#0000 98%,#000) -30px -30px 技巧是在左上角创建一个圆,通过用负偏移量移动它,我们覆盖了四个角落。将下方悬停在下方,看看这个技巧。


这个方法很完美,因为它使用一种渐变,且没有四舍五入的问题。但它有一个缺点,半径的值被使用5次。但是我们可以使用自定义属性进行简化:

--r: 30px; mask: radial-gradient(var(--r) at var(--r) var(--r),#0000 98%,#000) calc(-1*var(--r)) calc(-1*var(--r)) 让我们快速回顾一下我们刚刚介绍的三种方法:

  • 第一种方法使用四个渐变,在使用方面没有缺点,它适用于任何类型的元素和尺寸。但它的代码量很冗长。
  • 第二种方法使用一种渐变,但在某些情况下可能会有偏移不准确的情况。它适用于固定尺寸的元素。
  • 第三种方法使用一种渐变,没有四舍五入问题。这是其中的完美方法,但它需要在渐变值内多次使用半径。

只切割部分角落

现在我们已经看到了所有角落的案例,接下来让我们禁用其中一些。使用第一种方法,任何我们想保持未切割的角落,我们只需删除其渐变并调整剩余内容的大小。

要禁用右上角的时候:

  • 移除了右上角的渐变(蓝色渐变)。
  • 此时有一个空角落,所以增加了红色渐变(或紫色渐变)的大小,以覆盖剩余的空间。
在这里可以做多少可能性和组合。如果需要切割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()中的一种颜色更改为透明。例如,将右上角改为透明,就是下面的效果: 核心代码如下:

conic-gradient(#0000 25%,blue 0 50%,green 0 75%,purple 0) 通过将conic-gradient()的颜色从不透明更改为透明,可以实现我们想要切割的角落,并获得各种可能的组合。

  • 要切割四个角落,第一种方法需要四个渐变,而第三个方法只需要一个渐变,所以我们使用后者。
  • 要切割一个角落,第一种方法需要一个渐变,而第三个方法需要两个渐变,因此您可以使用第一个渐变。
  • 要切割两个角落,两者都使用两个渐变。
  • 要切割三个角落,一个方法将使用三个渐变,另一个方法仅使用两个渐变。
以上介绍了每个案例选择适当的方法,我们总共不需要超过两个渐变。但在实际应用中,您应该选择更优化简洁的代码。

圆形边框型切角

接下来实现和上面一样形状的纯边框版本。换句话说,我们要实现了相同的形状,但去掉了填充物,只剩下形状的边框。

这相对有些麻烦,在这里会使用很多渐变处理,同时尽可能的精简它们的数量。在这种情况下,将考虑使用伪元素。只显示边框意味着我们需要隐藏形状的内部“填充”。

一个角

如下图所示,则需要一个径向渐变和两个圆锥渐变: 图一说明了径向渐变(红色)和锥形渐变(蓝色和绿色)。图二中,我们将它们全部应用到CSS mask属性中,以创建一个切割角的纯边框形状。

如图所示,radial-gradient()创建圆的四分之一,每个conic-gradient()创建两个垂直段来覆盖两侧。使用相同的代码调整几个变量,我们可以获得其他角落的形状。

两个角

对于双角配置,我们有两个情况。

在第一种情况下,有两个相反的角落,我们需要两个径向渐变和两个圆锥渐变。

配置几乎与只切一个角落相同,我们添加额外的渐变并更新一些变量。核心代码如下:

--g: #0000 calc(98% - 10px),#000 calc(100% - 10px) 98%,#0000; --mask: radial-gradient(farthest-side at 0 0 ,var(--g)) 0 0/40px 40px no-repeat, conic-gradient(from 180deg at right 10px top 10px, #0000 0 90deg,#000 0) 100% 0/calc(100% - 40px + 10px) calc(100% - 40px + 10px) no-repeat, radial-gradient(farthest-side at 100% 100%,var(--g)) 100% 100%/40px 40px no-repeat, conic-gradient(from 0deg at left 10px bottom 10px, #0000 0 90deg,#000 0) 0 100%/calc(100% - 40px + 10px) calc(100% - 40px + 10px) no-repeat; background:linear-gradient(45deg,blue,red); mask: var(--mask); 在第二种情况下,有两个相邻的角,在这种情况下,我们需要一个径向渐变、一个圆锥渐变和一个线性渐变。 “等等!”你可能会惊呼。“为什么圆锥形渐变会覆盖三面?”。在所有示例中,我们总是使用no-repeat,但在这里,我们可以重复其中之一,以覆盖更多侧面并减少我们使用的渐变数量。所以这里我们使用了repeat-y。

另外一个你可能想知道一个径向渐变是如何切开两个角落的。为此,我们创建了半个圆圈,放在左上角。然后,通过使用负偏移量,我们切割了两个相邻的角落。可以看下方悬停动图了解这个技巧。

三个角

以此类推,对于这种配置,我们需要两个径向渐变、一个圆锥渐变和两个线性渐变。

四个角

那么切割所有四个角落则需要一个径向渐变和两个线性渐变。

你可能会想:“我该怎么记住所有这些箱子?!”你不需要记住任何东西,因为你可以使用在线生成器轻松为每个案例生成代码。您只需要了解整体技巧,而不是每个单独的案例。这就是为什么我只详细介绍了第一批配置——其余的只是调整该技巧初始基础的迭代。

请注意,在整个示例中,我们一直遵循一个总体模式:

我们在要切割的角落上添加一个radial-gradient()。 我们使用圆conic-gradient()linear-gradient()填充两侧,以创建最终形状。我们可以找到不同的方法来创建相同的形状。我在这篇文章中展示的是我在尝试了许多其他想法后发现最好的方法。如果你认为有更好的方法,欢迎留言分享!

角度切割

接下来我们解决另一种类型的切割形状:角度切割。

我们有两个参数:切割的大小和角度。为了获得形状,我们需要每个角落的conic-gradient()。这种配置与本文启动的示例非常相似。

以下是一个角落的实现技巧: 每个角落之间的差异是from位置和位置的额外偏移量为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 mask外,我们还可以使用CSS clip-path属性来切开角落。每个角落可以用三个点来定义。 其他角落的值相同,偏移量为100%。这给了我们总共12分的最终代码——每个角3分。

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注释。它定义了如果我们想禁用特定角落,我们必须考虑的代码。要切入一个角落,我们使用三点。要解开一个角落,我们使用一个点——它只不过是那个角落的坐标。

90deg特殊处理

当角度等于90deg,我们可以优化渐变版本的代码,并依赖更少的渐变。要切割四个角落,我们只能使用一个渐变:

--size: 30px; mask: conic-gradient(at var(--size) var(--size),#000 75%,#0000 0) 0 0/calc(100% - var(--size)) calc(100% - var(--size))

对于90deg,我们有两种渐变方法,第一种是我们之前详细说明了用一个渐变切割每个角落的方法,最后一种是我们使用一个渐变切割所有角落的方法。我想你知道故事的其余部分:为了解开一些角落,我们将最后一种方法与圆锥形渐变相结合。正如我所说,不需要记住所有的方法和技巧。生成器将为您生成代码,我只是试着让这篇文章尽可能详细,以涵盖所有可能的情况。

最后

我们只是将CSS mask与渐变相结合,以创建一些花哨的形状,而无需使用大量代码!我们还体验过,要达到正确的代码平衡才能获得正确的结果。在此过程中,我们甚至学到了一些技巧,例如将值更改为一个甚至半个单位。CSS超级强大!

正如我们所讨论的,我制作的在线生成器是获取您需要的代码的好地方,而不是手工编写。

看完本文如果觉得有用,记得点个赞支持,收藏起来说不定哪天就用上啦~

专注前端开发,分享前端相关技术干货,公众号:南城大前端(ID: nanchengfe)

有关基于CSS mask和clip-path实现切角的技巧的更多相关文章

  1. ruby - 如何根据特征实现 FactoryGirl 的条件行为 - 2

    我有一个用户工厂。我希望默认情况下确认用户。但是鉴于unconfirmed特征,我不希望它们被确认。虽然我有一个基于实现细节而不是抽象的工作实现,但我想知道如何正确地做到这一点。factory:userdoafter(:create)do|user,evaluator|#unwantedimplementationdetailshereunlessFactoryGirl.factories[:user].defined_traits.map(&:name).include?(:unconfirmed)user.confirm!endendtrait:unconfirmeddoenden

  2. 叮咚买菜基于 Apache Doris 统一 OLAP 引擎的应用实践 - 2

    导读:随着叮咚买菜业务的发展,不同的业务场景对数据分析提出了不同的需求,他们希望引入一款实时OLAP数据库,构建一个灵活的多维实时查询和分析的平台,统一数据的接入和查询方案,解决各业务线对数据高效实时查询和精细化运营的需求。经过调研选型,最终引入ApacheDoris作为最终的OLAP分析引擎,Doris作为核心的OLAP引擎支持复杂地分析操作、提供多维的数据视图,在叮咚买菜数十个业务场景中广泛应用。作者|叮咚买菜资深数据工程师韩青叮咚买菜创立于2017年5月,是一家专注美好食物的创业公司。叮咚买菜专注吃的事业,为满足更多人“想吃什么”而努力,通过美好食材的供应、美好滋味的开发以及美食品牌的孵

  3. 华为OD机试用Python实现 -【明明的随机数】 2023Q1A - 2

    华为OD机试题本篇题目:明明的随机数题目输入描述输出描述:示例1输入输出说明代码编写思路最近更新的博客华为od2023|什么是华为od,od薪资待遇,od机试题清单华为OD机试真题大全,用Python解华为机试题|机试宝典【华为OD机试】全流程解析+经验分享,题型分享,防作弊指南华为o

  4. 基于C#实现简易绘图工具【100010177】 - 2

    C#实现简易绘图工具一.引言实验目的:通过制作窗体应用程序(C#画图软件),熟悉基本的窗体设计过程以及控件设计,事件处理等,熟悉使用C#的winform窗体进行绘图的基本步骤,对于面向对象编程有更加深刻的体会.Tutorial任务设计一个具有基本功能的画图软件**·包括简单的新建文件,保存,重新绘图等功能**·实现一些基本图形的绘制,包括铅笔和基本形状等,学习橡皮工具的创建**·设计一个合理舒适的UI界面**注明:你可能需要先了解一些关于winform窗体应用程序绘图的基本知识,以及关于GDI+类和结构的知识二.实验环境Windows系统下的visualstudio2017C#窗体应用程序三.

  5. MIMO-OFDM无线通信技术及MATLAB实现(1)无线信道:传播和衰落 - 2

     MIMO技术的优缺点优点通过下面三个增益来总体概括:阵列增益。阵列增益是指由于接收机通过对接收信号的相干合并而活得的平均SNR的提高。在发射机不知道信道信息的情况下,MIMO系统可以获得的阵列增益与接收天线数成正比复用增益。在采用空间复用方案的MIMO系统中,可以获得复用增益,即信道容量成倍增加。信道容量的增加与min(Nt,Nr)成正比分集增益。在采用空间分集方案的MIMO系统中,可以获得分集增益,即可靠性性能的改善。分集增益用独立衰落支路数来描述,即分集指数。在使用了空时编码的MIMO系统中,由于接收天线或发射天线之间的间距较远,可认为它们各自的大尺度衰落是相互独立的,因此分布式MIMO

  6. kvm虚拟机安装centos7基于ubuntu20.04系统 - 2

    需求:要创建虚拟机,就需要给他提供一个虚拟的磁盘,我们就在/opt目录下创建一个10G大小的raw格式的虚拟磁盘CentOS-7-x86_64.raw命令格式:qemu-imgcreate-f磁盘格式磁盘名称磁盘大小qemu-imgcreate-f磁盘格式-o?1.创建磁盘qemu-imgcreate-fraw/opt/CentOS-7-x86_64.raw10G执行效果#ls/opt/CentOS-7-x86_64.raw2.安装虚拟机使用virt-install命令,基于我们提供的系统镜像和虚拟磁盘来创建一个虚拟机,另外在创建虚拟机之前,提前打开vnc客户端,在创建虚拟机的时候,通过vnc

  7. 动漫制作技巧如何制作动漫视频 - 2

    动漫制作技巧是很多新人想了解的问题,今天小编就来解答与大家分享一下动漫制作流程,为了帮助有兴趣的同学理解,大多数人会选择动漫培训机构,那么今天小编就带大家来看看动漫制作要掌握哪些技巧?一、动漫作品首先完成草图设计和原型制作。设计草图要有目的、有对象、有步骤、要形象、要简单、符合实际。设计图要一致性,以保证制作的顺利进行。二、原型制作是根据设计图纸和制作材料,可以是手绘也可以是3d软件创建。在此步骤中,要注意的问题是色彩和平面布局。三、动漫制作制作完成后,加工成型。完成不同的表现形式后,就要对设计稿进行加工处理,使加工的难易度降低,并得到一些基本准确的概念,以便于后续的大样、准确的尺寸制定。四、

  8. 【Java入门】使用Java实现文件夹的遍历 - 2

    遍历文件夹我们通常是使用递归进行操作,这种方式比较简单,也比较容易理解。本文为大家介绍另一种不使用递归的方式,由于没有使用递归,只用到了循环和集合,所以效率更高一些!一、使用递归遍历文件夹整体思路1、使用File封装初始目录,2、打印这个目录3、获取这个目录下所有的子文件和子目录的数组。4、遍历这个数组,取出每个File对象4-1、如果File是否是一个文件,打印4-2、否则就是一个目录,递归调用代码实现publicclassSearchFile{publicstaticvoidmain(String[]args){//初始目录Filedir=newFile("d:/Dev");Datebeg

  9. ruby - Arrays Sets 和 SortedSets 在 Ruby 中是如何实现的 - 2

    通常,数组被实现为内存块,集合被实现为HashMap,有序集合被实现为跳跃列表。在Ruby中也是如此吗?我正在尝试从性能和内存占用方面评估Ruby中不同容器的使用情况 最佳答案 数组是Ruby核心库的一部分。每个Ruby实现都有自己的数组实现。Ruby语言规范只规定了Ruby数组的行为,并没有规定任何特定的实现策略。它甚至没有指定任何会强制或至少建议特定实现策略的性能约束。然而,大多数Rubyist对数组的性能特征有一些期望,这会迫使不符合它们的实现变得默默无闻,因为实际上没有人会使用它:插入、前置或追加以及删除元素的最坏情况步骤复

  10. ruby-on-rails - 在 Rails 控制台中使用 asset_path - 2

    在我的Character模型中,我添加了:字符.rbbefore_savedoself.profile_picture_url=asset_path('icon.png')end但是,对于数据库中已存在的所有角色,它们的profile_picture_url为nil。因此,我想进入控制台并遍历所有这些并进行设置。在我试过的控制台中:Character.find_eachdo|c|c.profile_picture_url=asset_path('icon.png')end但这给出了错误:NoMethodError:undefinedmethod`asset_path'formain:O

随机推荐