CSS 遮罩简介

CSS 遮罩允许你通过为一个元素应用一个或多个遮罩图像,从而选择性地显示或隐藏该元素的部分内容。这些遮罩图像可以是渐变、图像或 SVG 源。与 CSS 裁剪不同——后者基于单一路径的形状来完全显示或隐藏元素的区域——遮罩可以基于遮罩图像的 Alpha 透明度以及(可选的)亮度,实现更细微的透明度和混合效果。

本指南介绍了遮罩的概念、各种遮罩图像类型,以及遮罩的亮度和 Alpha 透明度如何影响元素被遮罩(可见)的部分,以及被裁剪(或隐藏)的部分。

CSS 中的遮罩是什么?

在 CSS 中,遮罩可用于定义元素的可见区域和隐藏区域。由一个或多个 mask-image 源定义的遮罩层,决定了元素哪些区域应该可见以及其不透明度。

备注: 可以使用 mask 简写属性设置多个 CSS 遮罩属性值。

对于 alpha 遮罩,被遮罩元素的不透明度与应用的遮罩的不透明度相匹配。在 CSS 中,遮罩与化装舞会面具的作用相反,后者是在面具不透明的地方隐藏脸部。而在 CSS 中,元素中其遮罩完全不透明的区域将是完全不透明且可见的。在遮罩完全透明的地方,元素将完全隐藏。被部分不透明遮罩区域遮罩的元素区域将是部分不透明的,与其遮罩的不透明度相匹配。

对于 Alpha 遮罩,遮罩的颜色无关紧要,只有遮罩的不透明度才重要。而对于亮度遮罩,在确定被遮罩元素的不透明度时,会考虑遮罩颜色的亮度。颜色越亮、越不透明,元素就越不透明。颜色越暗、越透明,遮罩的不透明度就越低。

遮罩可以使用 CSS 渐变、栅格图像(如 PNG)和 SVG <mask> 元素来定义。在本指南中,我们将在讨论不透明度和透明度亮度以及遮罩与 CSS 裁剪时,介绍各种遮罩图像类型。

每个遮罩层都包含一个 mask-image,该图像相对于一个原点框进行定位。遮罩图像可以被调整大小重复裁剪。在声明了多个遮罩图像的情况下,可以设置遮罩层的合成或组合方式。这些内容将在遮罩属性指南中讨论。

备注: 所有示例都将使用以下图像作为应用遮罩的基础元素。

Pride flag

不透明度与透明度

对于 Alpha 遮罩,元素的可见区域由应用于其上的遮罩的 Alpha 透明度定义。在遮罩完全不透明的地方,元素将是可见的。在遮罩完全透明的每个像素点,元素也将完全隐藏。被遮罩的部分不透明区域遮罩的元素区域将是部分不透明的,与其所应用的遮罩的不透明度相匹配。

使用渐变

为了说明这一点,我们来看一个使用 conic-gradient() 作为 mask-image 的示例。CSS 渐变,包括锥形渐变,可用于在可见和隐藏区域之间创建平滑过渡。

在这种情况下,遮罩的右上角是完全不透明的,左上象限是完全透明的,下半部分则在不透明和透明之间平滑过渡。

css
.applied-mask {
  mask-image: conic-gradient(black 90deg, transparent 270deg);
}
.mask-source {
  background: conic-gradient(black 90deg, transparent 270deg);
}

请注意,应用了遮罩的元素的右上角是完全可见的,左上角被隐藏,下半部分则从可见平滑过渡到透明,这反映了所应用的遮罩图像的可见性。

对于 Alpha 遮罩,遮罩的颜色不重要,只有透明度才重要。在这个示例中,我们有一个条纹渐变,包含完全不透明的红色、半透明的红色和完全透明的条纹。

css
.applied-mask {
  mask-image: repeating-linear-gradient(
    to bottom right,
    red 0 20px,
    #ff000055 20px 40px,
    transparent 40px 60px
  );
}
.mask-source {
  background: repeating-linear-gradient(
    to bottom right,
    red 0 20px,
    #ff000055 20px 40px,
    transparent 40px 60px
  );
}

请注意,完全不透明的遮罩区域会显示完全不透明的元素像素,半透明的遮罩区域会创建半透明的区域,而完全透明的遮罩区域会完全隐藏相关区域。

使用导入的图像

前两个示例使用渐变作为遮罩和背景图像。遮罩图像不一定是 CSS 图像,也可以是外部图像或 SVG。

在这种情况下,我们使用一个外部 PNG 图像。该图像包含一个带有透明背景的彩色心形。

css
.applied-mask {
  mask-image: url("https://mdn.github.io/shared-assets/images/examples/colorful-heart.png");
  mask-size: 220px 220px;
}
.mask-source {
  background: url("https://mdn.github.io/shared-assets/images/examples/colorful-heart.png");
  background-size: 220px 220px;
}

请注意透明的遮罩区域如何裁剪元素;元素中唯一可见的部分是遮罩不透明的区域。遮罩本身的颜色无关紧要。

Alpha 透明度与亮度

mask-mode 属性的默认值——match-source——会根据具体值将模式设置为 alphaluminance。对于除 SVG <mask> 元素外的所有遮罩源,match-source 值会解析为 alpha。如果遮罩源是 <mask> 元素,match-source 会解析为该 <mask>mask-type 属性值(如果已设置)。否则,它会解析为在 <mask> 元素上设置的 SVG mask-type 属性的值。如果该属性也未明确设置,match-source 将解析为 luminance

如果 mask-mode 解析为 luminance,或者我们明确将其设置为 luminance,遮罩的颜色将影响遮罩的不透明度。在前面的演示中,没有设置 mask-mode,因此其值默认为 match-source。由于彩色心形图像是透明的 PNG,match-source 解析为 alpha。通过明确设置此属性,我们可以控制模式。在此演示中,我们将 mask-mode 更改为 luminance

css
.applied-mask {
  mask-mode: luminance;
}

当对与前一个示例相同的遮罩应用 mask-mode: luminance 时,元素中遮罩颜色最亮的区域更不透明,而较暗的区域则不那么不透明。

亮度遮罩的不透明度由 RGB 颜色的 RGBA 值通过以下公式确定:

((0.2125 * R) + (0.7154 * G) + (0.0721 * B)) * A

例如,最新的 <named-color>rebeccapurple,即 #663399。虽然人们可能认为其亮度可能等同于 hsl() 颜色函数的 L 值,但并非如此简单。值 #663399 等同于 rgb(40% 20% 60% / 1)hsl(270 50% 40% / 1),但其亮度值为 27.134%,而不是 40%

((0.2125 * 0.4) + (0.7154 * 0.2) + (0.0721 * 0.6)) * 1 = 0.27134

白色的亮度值为 100%

((0.2125 * 1) + (0.7154 * 1) + (0.0721 * 1)) * 1 = 1

黑色的亮度为 0%

((0.2125 * 0) + (0.7154 * 0) + (0.0721 * 0)) * 1 = 0

我们将通过在一个包含 rebeccapurplewhiteblack 的线性渐变中,添加不透明度为 27.234% 的白色(rgb(100% 100% 100%),其亮度为 100%)来演示这一点,然后用这个渐变来遮罩我们的图像。这个白色会解析为相同的不透明度值。

((0.2125 * 1) + (0.7154 * 1) + (0.0721 * 1)) * 0.27134 = 0.27134

css
.applied-mask {
  mask-image: repeating-linear-gradient(
    to bottom left,
    rebeccapurple 0 20px,
    rgb(100% 100% 100% / 0.27134) 20px 40px,
    black 40px 45px,
    white 45px 50px
  );
  mask-mode: luminance;
}
.mask-source {
  background: repeating-linear-gradient(
    to bottom left,
    rebeccapurple 0 20px,
    rgb(100% 100% 100% / 0.27134) 20px 40px,
    black 40px 45px,
    white 45px 50px
  );
}

white 遮罩的区域是完全不透明的。black 遮罩的区域是完全透明的。rebeccapurple 遮罩的区域和不透明度为 27.1234% 的白色遮罩的区域,其不透明度均为 27.1234%

如果将 mask-mode 切换为 alpha,渐变的颜色将不再重要。除了被半透明白色覆盖的区域外,整个元素都将是不透明的。

mask-mode 属性使得可以使用没有 Alpha 透明度的栅格图像(如 JPEG)作为遮罩图像。JPEG 由不透明像素组成。如果使用 JPEG 作为遮罩并采用其默认的 alpha 遮罩模式,将会隐藏整个元素。另一方面,mask-modeluminance 值会在遮罩为黑色(没有亮度)的地方裁剪元素,在遮罩为不透明白色(100% 亮度)的地方完全不透明,其他区域则根据遮罩区域的亮度呈现半透明状态。

在这个示例中,我们有一个白色月亮悬挂在黑色夜空中的图像。

css
.applied-mask {
  mask-image: url("https://mdn.github.io/shared-assets/images/examples/moon.jpg");
  mask-mode: luminance;
  mask-size: 220px;
}
.mask-source {
  background: url("https://mdn.github.io/shared-assets/images/examples/moon.jpg");
  background-size: 220px;
}

在天空是黑色的地方,元素被裁剪且不可见。在月亮最亮的地方,图像最为可见。

在这种情况下,如果将 mask-mode 切换为 alpha,整个元素将变得可见,因为整个遮罩都是不透明的。

SVG <mask> 作为遮罩源

遮罩可以是任何类型的 CSS <image><mask-source><mask-source> 是一个指向 SVG <mask> 元素的 <url> 引用。这与使用 CSS clip-path 属性进行裁剪类似,在该情况下,“遮罩”是一个 SVG <clipPath> 元素(对于 clip-path,路径的亮度无关紧要)。

在这个示例中,我们定义了一个包含 <mask> 元素、一个相同的 <clipPath> 元素和一个相同的 <path> 元素的 SVG,以便你可以查看遮罩和裁剪路径的源。

html
<img
  src="https://mdn.github.io/shared-assets/images/examples/progress-pride-flag.jpg"
  alt="Pride flag"
  class="applied-mask" />
<svg class="mask-source">
  <mask id="mask-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"
      fill="rgb(255 0 0 / 0.5)"
      stroke="rgb(255 255 255 / 1)"
      stroke-width="20" />
  </mask>
  <clippath id="clip-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"
      fill="rgb(255 0 0 / 0.5)"
      stroke="rgb(255 255 255 / 1)"
      stroke-width="20" />
  </clippath>
  <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"
    fill="rgb(255 0 0 / 0.5)"
    stroke="rgb(255 255 255 / 1)"
    stroke-width="20" />
</svg>
<img
  src="https://mdn.github.io/shared-assets/images/examples/progress-pride-flag.jpg"
  alt="Pride flag"
  class="applied-clip" />
css
.applied-mask {
  mask-image: url("#mask-heart");
}
.applied-clip {
  clip-path: url("#clip-heart");
}

因为图像源是 <mask>,且该遮罩既没有设置 mask-type CSS 属性,也没有设置 mask-type SVG 属性,所以 mask-type 默认为 alpha,因此 mask-mode: match-source 的默认值会解析为 luminance。这是因为,对于作为 SVG <mask> 元素的遮罩源,mask-type 默认为 luminance,除非 mask-type 属性被明确设置为 alpha

由于我们没有在遮罩上设置 mask-type 属性或 CSS 属性,mask-mode 属性的默认值 match-source 会解析为 luminance。切换复选框以将 mask-mode 值设置为 alpha 或让其默认为 match-source

这个示例还展示了 CSS 中遮罩和裁剪的区别。你会注意到,亮度和 Alpha 透明度与遮罩相关,但与裁剪无关。遮罩可用于控制元素的不透明度,而裁剪则显示裁剪路径内的所有内容,并完全隐藏裁剪路径外的元素部分。被裁剪的区域是完全不可见的,而被遮罩的区域可以是部分或完全可见的。

如果你只需要形状,裁剪可能就足够了。但如果你需要渐变、可变不透明度,甚至控制位置和大小(我们将在另一篇指南中讨论),那么遮罩更合适。

另见