路径、形状、剪裁和遮罩

HTML 渲染基于盒子模型构建,但生活(和网页设计)不仅仅是矩形。CSS 支持多种方式来更改元素的渲染区域,让开发者可以自由创建支持各种形状和尺寸的设计。裁剪允许使用几何形状,而遮罩会影响像素级的可见性。

路径和形状

CSS 使用函数来定义形状。我们在 CSS 函数模块中介绍了有关函数的一般信息。在本部分中,您将学习如何在 CSS 中创建形状。以下所有示例都使用您通过 clip-path 属性创建的形状,这会将可见区域缩小到仅包含形状内部的区域。这样,元素在视觉上就可以与元素的框有所不同。稍后我们会更详细地介绍剪辑。

在 CSS 中定义的形状可以是基本形状(例如圆形、矩形和多边形),也可以是路径(可用于定义复杂形状和复合形状)。

基本形状

circle()ellipse()

circle()ellipse() 函数用于定义半径相对于元素的圆形和椭圆形。circle() 函数接受单个大小或百分比作为实参。默认情况下,这两个函数都会将形状定位在元素的中心。两者都接受 at 关键字后面的可选位置,该位置可以表示为长度、百分比或位置关键字。

.my-element {
  width: 60px;
  height: 60px;
  background: blue;
  clip-path: circle(50%);
}

当 circle() 函数的实参为 50% 时,系统会渲染一个正圆。

上例展示了使用 circle() 函数的圆形剪切路径。请注意,半径为 50% 时会创建一个与元素宽度相同的圆。ellipse() 函数接受两个实参,分别表示形状的水平半径和垂直半径。

.my-element {
  width: 60px;
  height: 60px;
  background: blue;
  clip-path: ellipse(50% 25%);
}

ellipse() 函数通过百分比实参生成椭圆。参数为 50% 和 25% 时,会生成一个椭圆,其 X 轴上的半径是 Y 轴上的两倍。

上例展示了使用 ellipse() 函数的椭圆剪切路径。请注意,半径为 50% 时,会创建一个宽度与元素宽度相同的椭圆。以下示例展示了中心位于元素顶部的同一椭圆。

.my-element {
  width: 60px;
  height: 60px;
  background: blue;
  clip-path: ellipse(50% 25% at center top);
}

rect()inset()

rect()inset() 函数提供了不同的方式来定义矩形,即通过设置矩形各边相对于元素各边的位置来定义。这样,您就可以创建与元素的默认框在视觉上不同的矩形。它们可以选择性地接受 round 关键字,以创建圆角矩形,使用与 border-radius 简写属性相同的语法。

rect() 函数用于定义矩形顶部和底部相对于元素上边缘的位置,以及左侧和右侧相对于元素左边缘的位置。此函数接受四个大小或百分比单位作为实参,用于定义上、右、下和左侧。如果您希望矩形在元素大小发生变化时不会缩放,或者希望矩形在元素大小发生变化时保持相同的比例,则可以选择 rect() 函数。

.my-element {
  width: 80px;
  height: 60px;
  background: blue;
  clip-path: rect(15px 75px 45px 10px);
}

rect() 函数接受四个实参来定义矩形的大小。在本例中,实参为 15px、75px、45px 和 10px。

上例展示了使用 rect() 函数定义的矩形剪切路径。这些维度相对于元素的顶部边缘和左侧边缘,如图所示。

inset() 函数通过从元素的每个边向内测量的距离来定义矩形各边的位置。此函数接受一到四个尺寸或百分比单位作为实参,让您能够一次性定义多个边。如果您希望矩形随元素缩放,或者希望矩形与元素的边缘保持固定距离,则可以选择 inset() 函数。

.my-element {
  width: 80px;
  height: 60px;
  background: blue;
  clip-path: inset(15px 5px 15px 10px);
}

inset() 函数可以从元素的固有尺寸中减去值。此图中的相应实参为 15 像素、5 像素、15 像素和 10 像素。

上例展示了使用 inset() 函数定义的矩形剪切路径。尺寸相对于元素各边。

rect()inset() 函数可以选择性地接受 round 关键字,以创建圆角矩形,使用与 border-radius 简写属性相同的语法。以下示例展示了之前显示的矩形的圆角版本。

.rounded-rect {
  width: 80px;
  height: 60px;
  background: blue;
  clip-path: inset(15px 5px 15px 10px round 5px);
}

.rounded-inset {
  width: 80px;
  height: 60px;
  background: blue;
  clip-path: inset(15px 5px 15px 10px round 5px);
}

polygon()

对于其他形状(例如三角形、五边形、星形等),您可以使用 polygon() 函数通过直线连接多个点来创建形状。polygon() 函数接受一个列表,其中包含由两个长度或百分比单位组成的对。每对值都描述了多边形上的一个点:第一个值表示与元素左边缘的距离,第二个值表示与元素上边缘的距离。您无需闭合多边形,因为系统会将最后一个点与第一个点相连,从而完成多边形。

.my-element {
  width: 60px;
  height: 60px;
  background: blue;
  clip-path: polygon(
    50% 0,
    0 100%,
    100% 100%
  );
}

polygon() 函数接受可变数量的实参,以便绘制复杂的形状。在这种情况下,参数经过精心设计,可创建三角形。

上述示例通过定义三个点创建了一个三角形剪切路径。

默认情况下,polygon() 函数会将重叠区域渲染为填充区域。您可以使用名为填充规则的可选第一个实参来更改此行为。如需在填充区域和非填充区域之间交替,请将填充规则设置为 evenodd。如需使用默认填充规则,请将其设置为 nonzero

上例展示了如何使用 polygon() 函数和三角函数来创建正多边形和星形。这不会创建适合元素内部或以元素为中心的最大可能正多边形,我们将此作为练习留给您尝试。此示例中的星形还演示了 nonzeroevenodd 填充规则。

复杂形状

如果基本形状函数不足以描述复杂形状,CSS 会提供使用更复杂语法来描述曲线和线条等特征的函数。这些函数对于复合形状(由多个形状组成的形状,例如带孔的圆形)也很有用。

path()

path() 函数接受一个 SVG 路径语法字符串来描述形状。这样便可使用描述构成形状的直线和曲线的指令来创建复杂形状。直接编辑 SVG 语法可能很复杂,因此我们建议您考虑使用专用可视化编辑器,以便在通过 path() 函数创建形状时导出语法。

path() 函数不使用 CSS 大小调整单位,所有值都以像素为单位进行解读。这意味着使用路径函数创建的形状不会根据元素或容器的大小做出相应调整。我们建议仅将 path() 用于具有固定尺寸的形状。

shape()

shape() 函数使用命令语法来描述形状,类似于 path() 函数。不过,shape() 函数命令是原生 CSS,可以使用 CSS 大小单位。这样一来,使用 shape() 函数定义的形状就可以自适应调整大小。

上例使用 path()shape() 函数定义了一个心形和一个中心有孔的圆形。此示例对这两个函数使用了相同的像素值,但 shape() 函数也可以使用其他 CSS 大小单位,例如百分比或容器相对单位。

裁剪

剪裁用于定义元素的哪些区域可见,类似于从杂志上剪裁图片。clip-path 属性用于设置定义裁剪区域的路径。

正如您在前面部分中的示例中所见,任何基本形状或路径函数都可以用作 clip-pathclip-path 属性还支持在 SVG clipPath 元素中定义的路径,这些路径可以嵌入到 SVG 元素中,也可以位于单独的文件中。

剪切如何影响图片(尤其是):在此图片中,小猫的照片被剪切成圆形,以及一个完整勾勒出小猫轮廓的复杂剪切路径。

上图显示了向图片元素添加 clip-path 后,图片的可见区域会发生怎样的变化。上方的剪切路径使用 circle() 函数,而下方的剪切路径使用 SVG clipPath。请注意,使用 circle() 函数创建的圆默认位于元素的中心。

clip-path 属性仅接受单个路径。如需使用多个不重叠的形状来剪裁元素,请使用 path()shape() 函数定义复合路径,或使用 SVG clipPath。对于复杂场景,另一种选择是使用遮罩而不是剪裁,我们将在后面的部分中介绍这一点。

使用形状进行剪裁

如需使用基本形状或路径函数进行剪裁,请将 clip-path 属性设置为函数返回的值,如上例所示。每个函数都会以不同的方式定位相对于元素的剪切形状,因此请参阅每个函数的参考文档。

在上述示例中,有两个元素使用 .clipped 类应用了圆形 clip-path。请注意,clip-path 是相对于每个元素定位的,并且 clip-path 中的文字不会重新排版以贴合形状。

剪切路径的参考框

默认情况下,元素的剪切路径包含元素的边框。使用某个基本形状函数时,您可以将剪切路径的参考框设置为仅包含元素边框内的区域。参考框的有效值为 stroke-box(默认值)和 fill-box(仅包含边框内的区域)。

上例展示了具有较大(20px 边框)的元素,每个元素都使用 inset() 函数来设置 clip-path。相对于元素边框进行裁剪的元素仍会显示部分边框。相对于边框内区域进行剪裁的元素不会显示任何边框,并且即使具有相同的内边距值,也会更小。

使用图形进行剪裁

可以在 SVG 文档中定义剪切路径,该路径可以嵌入在 HTML 文档中,也可以在外部引用。这对于定义在图形程序中创建的复杂剪切路径或组合了多种形状的剪切路径非常有用。

<img id="kitten" src="kitten.png">

<svg>
  <defs>
    <clipPath id="kitten-clip-shape">
      <circle cx="130" cy="175" r="100" />
    </clipPath>
  </defs>
</svg>

<style>
  #kitten {
    clip-path: url(#kitten-clip-shape);
  }
</style>

在上面的示例中,idkitten-clip-shapeclipPath 应用于 <img> 元素。在这种情况下,SVG 文档会嵌入到 HTML 中。如果 SVG 文档是名为 kitten-clipper.svg 的外部文件,则 clipPath 将改为引用为 url(kitten-clipper.svg#kitten-clip-shape)

遮盖

遮罩是另一种用于定义元素的哪些区域显示或隐藏的方法。裁剪使用基本形状或路径,而遮罩使用图片或渐变的像素来确定可见性。与剪裁不同,遮罩允许元素的部分区域具有半透明效果。您可以将多个遮罩图片应用于一个元素,以产生各种效果。

如需应用遮罩,请设置 mask-image 属性。此属性接受一个或多个图片、渐变或对 SVG 文档中 <mask> 元素的引用。您可以使用英文逗号分隔多个遮罩图片,以应用多个遮罩图片。

.my-element {
  mask-image: url(my-mask.png),
              linear-gradient(black 0%, transparent 100%);
}

在上述示例中,.my-element 使用 PNG 图片进行遮盖,然后使用线性渐变。默认情况下,系统会将多个遮罩相加,以创建最终遮罩。

上例展示了应用了一个或多个遮罩的图片。切换每个遮罩,查看遮罩如何相加以产生最终效果。

Alpha 遮罩与亮度遮罩

您可以使用图片的 alphaluminance 应用遮罩。如果基于 alpha 进行遮盖,系统会将遮盖图片中每个像素的透明度应用于相应元素,并忽略该像素的任何颜色信息。如果基于 luminance 进行遮罩,则每个像素的透明度和值(亮度或暗度)都会应用于相应元素。亮度遮罩功能会将较亮的颜色视为可见,而将较暗的颜色视为不可见。

如需设置遮盖模式,请使用 mask-mode 属性。默认情况下,mask-mode 属性设置为 match-source,这会根据遮罩图片的类型设置模式。对于图片和渐变,此属性的默认值为 alpha。对于 SVG 遮罩,此属性将默认设置为 <mask> 元素的 mask-type 属性的值,如果没有定义 mask-type,则设置为 luminance

在上述示例中,显示不同颜色和 Alpha 值的测试图案用作遮罩。通过切换 mask-mode,您可以了解 alpha 模式如何基于透明度,而 luminance 模式如何同时基于颜色亮度和透明度。

其他遮盖属性

CSS 提供了其他属性来微调遮罩的行为。每个属性都接受以英文逗号分隔的值列表,这些值将与 mask-image 属性设置的掩码列表进行匹配。如果值的数量少于掩码的数量,系统会重复使用这些值,直到为每个掩码都设置了值。如果值的数量多于掩码的数量,则会舍弃所有多余的值。

属性 说明
mask-clip

设置元素遮罩所应用的哪个参考框。默认设置为 border-box.

mask-composite

设置将多个遮罩应用于同一元素时,遮罩之间的互动。默认为 add

mask-origin

设置充当遮罩原点的参考框。默认为 border-box。此属性的行为与 background-origin 类似,并接受相同的关键字。

mask-position

设置遮罩相对于 mask-origin 的位置。接受位置关键字值(例如 topcenter)、百分比、尺寸单位或相对于位置关键字的值。此行为与 background-position 类似,并接受相同的实参类型。

mask-repeat

设置当被遮罩的元素大于遮罩时,遮罩的重复方式。默认为 repeat。此行为与 background-repeat 类似,并接受相同的实参类型。

mask-size

设置遮罩相对于被遮盖元素的大小调整方式。默认为 auto。此行为与 background-size 类似,并接受相同的实参类型。

遮罩简写

您可以使用遮罩简写形式一次性设置多个遮罩属性。这样一来,您就可以将每个遮罩的所有属性分组在一起,从而简化多个遮罩的设置。遮罩简写形式相当于按顺序设置以下属性:mask-imagemask-modemask-positionmask-sizemask-repeatmask-originmask-clipmask-composite。并非所有属性都需要包含在其中,任何未包含的属性都将重置为初始值。每个掩码最多支持 8 个属性,因此最好有完整参考

.longhand {
  mask-image: linear-gradient(white, black),
              linear-gradient(90deg, black, transparent);
  mask-mode: luminance, alpha;
  mask-position: bottom left, top right;
  mask-size: 50% 50%, 30% 30%;
}

.shorthand {
  mask: linear-gradient(white, black) luminance bottom left / 50% 50%,
        linear-gradient(90deg, black, transparent) alpha top right / 30% 30%;
}

在上面的示例中,每个类都应用了两个遮罩。第一个使用各个属性,而第二个使用 mask 简写。这两种样式是等效的。

环绕浮动元素的灵活格式文本

剪切或遮盖元素时,您只会更改其框内的可见区域,但框本身保持不变。这意味着浮动元素会根据其原始边界框(而非元素的可见部分)影响文档流。如需定义元素周围的流,请将 shape-outside 属性与裁剪路径搭配使用。

shape-outside 属性用于定义内容将围绕元素流动的形状。此形状可以是任何基本形状函数,但不能是使用 path()shape() 函数定义的形状,也不能是 SVG 文档中定义的 clipPath

shape-outside 属性还接受图片或渐变。与遮罩一样,形状的边界将由图片或渐变的透明度决定。shape-image-threshold 属性用于设置哪些透明度级别被视为位于形状内。

动画中的形状

为裁剪路径添加动画效果

您可以为 clip-path 属性添加动画效果,实现从一个形状到另一个形状的混合。您必须为每个关键帧使用相同的形状函数,才能制作出流畅的动画。使用 polygon()shape() 函数时,每个关键帧中必须使用相同数量的点。

在上述示例中,元素的 clip-path 在使用 polygon() 函数定义的五边形和星形之间转换。此示例使用 evenodd 填充规则来展示动画点如何创建重叠区域。

使用 offset-path 属性添加动画效果

您还可以沿使用这些形状函数创建的路径为元素添加动画效果。offset-path 属性用于设置用作路径的形状,而 offset-distance 用于设置沿该路径的位置。您还可以将 ray() 函数与 offset-path 属性搭配使用,以沿直线添加动画效果。

上述示例演示了如何将同一多边形同时用于 clip-pathoffset-path。动画使用 offset-distance 沿与大星相同的多边形移动小星,该多边形用作大星的 clip-path

检验您的掌握情况

以下哪些是有效的形状函数?

circle()
正确!
square()
错误。
hexagon()
错误。
polygon()
正确!
rectangle()
错误。
inset()
正确!

判断正误:使用 path() 函数定义的形状可以使用百分比来定义

正确
错误。
错误
正确!

判断对错:设置元素的剪切路径不会改变元素周围的文字排布

正确
正确!
错误
错误。

以下哪项可用作剪切路径?

基本形状
正确!
SVG clipMask 元素
正确!
位图图像
错误。
渐变
错误。

以下哪项可用作遮罩?

位图图像
正确!
渐变
正确!
SVG 蒙版元素
正确!
基本形状,例如 circle()rect()
错误。