CSS 裁剪简介
CSS 裁剪可以让你定义元素的可见部分,同时隐藏其他部分,从而有效地将元素内容“裁剪”成特定的形状或区域。通过裁剪,元素不再局限于渲染成矩形,可以设计出更具视觉吸引力的样式。本指南将探讨 clip-path
属性以及一些示例。
CSS 裁剪
裁剪是一种 CSS 技术,用于剪切(隐藏)元素的部分区域,仅显示位于开发者定义的路径内的元素区域。剪切区域由矢量路径创建;路径内的任何内容都可见,而路径外的内容则被隐藏。
clip-path
属性
clip-path
属性用于应用裁剪。它接受的值是一个矢量路径,定义了元素中应该保持可见的区域。该路径可以使用盒子(box)、对 SVG <clipPath>
的引用或 CSS 形状和路径来定义。在下面的示例中,我们使用 polygon()
函数作为裁剪路径,将一个蓝色的方形 <div>
裁剪成一个菱形。
.diamond {
height: 200px;
width: 200px;
background-color: blue;
clip-path: polygon(0 50%, 50% 100%, 100% 50%, 50% 0);
}
通过 clip-path
属性,你可以将元素裁剪为 <basic-shape>
或 SVG 源,从而创建复杂的形状。如果声明的各个状态具有相同数量的矢量点,你还可以对 clip-path
形状进行动画和过渡。
clip-path
属性的值
要对元素进行视觉裁剪,clip-path
属性可以设置为一个 <geometry-box>
、一个指向 <clipPath>
裁剪源的 url
,或一个用形状函数创建的 <basic-shape>
。
几何盒子
clip-path
会隐藏裁剪区域之外的所有内容。最基本的裁剪是通过几何盒子完成的。你可以根据元素的外边距、边框、内边距或内容进行裁剪。这些视觉盒子值的效果可以通过其他 CSS 属性实现,例如将 border-color
设置为透明,并将 background-origin
设置为所需的视觉盒子。我们主要关注这些值,是因为它们与我们稍后将要讨论的形状函数结合使用,以定义形状裁剪路径的原点。
在使用 clip-path
时,特别是与基本形状一起使用时,理解 CSS 形状所使用的参考盒非常重要,因为参考盒定义了形状的坐标系。
视觉盒子值
这个实时示例演示了 clip-path
属性对一个元素的不同视觉盒子值的效果,并将其与 CSS background-origin
属性进行比较。我们对 <blockquote>
应用了 border
、background-color
、background-image
和 padding
。选择一个单选按钮来将 --value
更新为不同的 <geometry-box>
值,这将更新 background-origin
和 clip-path
的解析值。
blockquote {
width: 210px;
padding: 20px;
margin: 20px;
border: 20px dashed #dedede;
background-color: #ededed;
background-image: linear-gradient(rebeccapurple, magenta);
background-repeat: no-repeat;
}
.clippath {
background-origin: var(--value);
clip-path: var(--value);
}
.box-model {
background-origin: var(--value);
}
当 <geometry>
盒子与 <basic-shape>
结合指定时,该值定义了基本形状的参考盒。如果单独指定,它将使指定盒子的边缘(包括任何圆角效果,如 border-radius
)成为裁剪路径。
形状原点
前面的示例可能会让你觉得 <geometry-box>
值没什么用,因为你可以用 background-origin
来代替。确实可以。但是,在使用基本形状进行裁剪时,如果 <geometry-box>
与 <basic-shape>
一起作为 clip-path
的值,它就定义了该形状的参考盒或原点。我们可以结合前面两个示例来演示这一点。
blockquote {
width: 210px;
padding: 20px;
margin: 20px;
border: 20px dashed #dedede;
background-color: #ededed;
background-image: linear-gradient(rebeccapurple, magenta);
background-repeat: no-repeat;
background-origin: border-box;
clip-path: var(--value) polygon(0 50%, 50% 100%, 100% 50%, 50% 0);
}
另一个示例,请参阅 clip-path
形状和几何盒子。
即使像 clip-path: margin-box
这样的值也很有用。除了通过将裁剪路径的边缘置于外边距盒边缘来创造视觉效果外,clip-path
的任何非 none
的计算值都会创建一个新的层叠上下文,就像 CSS opacity
属性在值不为 1
时所做的那样。
裁剪为基本形状
clip-path
属性对 <basic-shape>
值的支持提供了一种强大的元素塑形方式。各种形状函数可以定义精确的裁剪区域,有效地将元素塑造成独特的形态。基本形状函数包括:
这些形状的大小和位置由 <geometry-box>
值定义,如果 clip-path
值包含一个形状但没有 <geometry-box>
组件值,则默认使用 border-box 作为参考盒。
其中一些函数似乎只提供基本的预定义裁剪选项。它们可能看起来只是复制了你可以用 border-radius
创建的效果,但如果你在前面的示例中切换了 border-radius
属性,你可能已经注意到了 CSS 裁剪的强大之处。形状提供了更多的控制。例如,inset()
允许用精确的外边距来裁剪一个矩形。而真正的强大和控制力来自于 path()
、shape()
甚至 polygon()
,它们允许创建自定义的多点形状。
创建多边形
使用 polygon()
,通过定义代表形状每个顶点的坐标对,你可以创建复杂的形状,如星星或抽象图形。这些坐标定义了由直线连接的矢量点。
这里我们使用 polygon()
函数来创建一个星星:
.star {
width: 200px;
height: 200px;
background: linear-gradient(rebeccapurple, magenta) blue;
clip-path: polygon(
50% 0%,
61% 35%,
100% 35%,
68% 57%,
79% 91%,
50% 70%,
21% 91%,
32% 57%,
0% 35%,
39% 35%,
50% 0%
);
}
Animation
通过为不同状态声明相同数量的矢量点,可以对裁剪的形状进行动画和过渡。
@keyframes morphStar {
from {
clip-path: polygon(
50% 0%,
61% 35%,
100% 35%,
68% 57%,
79% 91%,
50% 70%,
21% 91%,
32% 57%,
0% 35%,
39% 35%,
50% 0%
);
}
to {
clip-path: polygon(
50% 10%,
65% 30%,
90% 20%,
75% 60%,
85% 95%,
50% 80%,
15% 95%,
25% 60%,
10% 20%,
35% 30%,
50% 10%
);
}
}
.star {
animation: morphStar alternate 3s infinite ease-in-out;
}
path()
函数
path()
函数可以使用 SVG 命令来绘制形状。该函数接受与 SVG d
属性等效的字符串作为其参数。
前一个示例中的星星可以用 path()
来创建:
.star {
width: 200px;
height: 200px;
background: linear-gradient(rebeccapurple, magenta) blue;
clip-path: path(
"M100,0 L122,70 L200,70 L136,114 L158,182 L100,140 L42,182 L64,114 L0,70 L78,70 L100,0 Z"
);
}
曲线
使用 path()
,我们不局限于直线。在这个例子中,我们使用 path()
函数来创建一个心形:
.heart {
width: 200px;
height: 200px;
background: linear-gradient(rebeccapurple, magenta) blue;
clip-path: path(
"M20,70 A40,40,0,0,1,100,70 A40,40,0,0,1,180,70 Q180,130,100,190 Q20,130,20,70 Z"
);
}
将 SVG 作为源
除了将 SVG d
属性字符串作为 path()
函数的参数传递外,clip-path
属性的值还可以直接引用 SVG <clipPath>
元素。
<div class="heart"></div>
<svg height="0" width="0">
<clipPath id="heart">
<path
d="M20,70 A40,40,0,0,1,100,70 A40,40,0,0,1,180,70 Q180,130,100,190 Q20,130,20,70 Z" />
</clipPath>
</svg>
<clipPath>
的 id
是 url()
函数的参数。
.heart {
width: 200px;
height: 200px;
background: linear-gradient(rebeccapurple, magenta) blue;
clip-path: url("#heart");
}
shape() 函数
SVG 路径语法并不是最直观的。因此,CSS 还提供了 shape()
函数。shape()
函数也接受路径绘制指令,但其语法更易于人类阅读。我们可以用更具声明性的 CSS 来重现心形:
.heart {
clip-path: shape(
from 20px 70px,
arc to 100px 70px of 1% cw,
arc to 180px 70px of 1% cw,
curve to 100px 190px with 180px 130px,
curve to 20px 70px with 20px 130px
);
}
shape()
函数更为强大,因为它接受 CSS 的值和单位(path()
仅限于坐标),包括使用像 calc()
这样的 CSS 数学函数。通过使用变量,我们可以创建多种不同大小的形状(和盒子):
:root {
--m: 10;
}
.heart {
width: calc(20px * var(--m));
height: calc(20px * var(--m));
display: inline-block;
background: linear-gradient(rebeccapurple, magenta) blue;
clip-path: border-box
shape(
from calc(2px * var(--m)) calc(7px * var(--m)),
arc to calc(10px * var(--m)) calc(7px * var(--m)) of 1% cw,
arc to calc(18px * var(--m)) calc(7px * var(--m)) of 1% cw,
curve to calc(10px * var(--m)) calc(19px * var(--m)) with
calc(18px * var(--m)) calc(13px * var(--m)),
curve to calc(2px * var(--m)) calc(7px * var(--m)) with
calc(2px * var(--m)) calc(13px * var(--m))
);
}
.small {
--m: 4;
}
.medium {
--m: 8;
}
.large {
--m: 12;
}
<div class="heart small"></div>
<div class="heart medium"></div>
<div class="heart large"></div>
让文本环绕裁剪的形状
被裁剪的元素仍然是矩形盒子。裁剪意味着你的元素看起来不像一个盒子,但它仍然是一个盒子。要让内联内容环绕你定义的非矩形(或矩形)形状,请使用 shape-outside
属性。默认情况下,内联内容会环绕其外边距盒;shape-outside
提供了一种自定义这种环绕的方式,使得文本可以围绕你裁剪的元素,并遵循你复制的裁剪路径,而不是元素的矩形盒子。
内容包括两个要裁剪的元素,以及将围绕它们进行塑形的文本。
<div class="leftTriangle"></div>
<div class="rightTriangle"></div>
<blockquote>
<q>
I've learned that people will forget what you said, people will forget what
you did, but people will never forget how you made them feel.</q
>
<cite>— Maya Angelou</cite>
</blockquote>
除了为 clip-shape
和 shape-outside
属性应用相同的形状外,被裁剪的元素还必须进行浮动,以便它与内容位于同一行。
.leftTriangle {
clip-path: polygon(0 0, 0 100%, 100% 0);
shape-outside: polygon(0 0, 0 100%, 100% 0);
float: left;
}
.rightTriangle {
clip-path: polygon(100% 0, 100% 100%, 0 100%);
shape-outside: polygon(100% 0, 100% 100%, 0 100%);
float: right;
}