CSS 遮罩简介
CSS 遮罩允许你通过为一个元素应用一个或多个遮罩图像,从而选择性地显示或隐藏该元素的部分内容。这些遮罩图像可以是渐变、图像或 SVG 源。与 CSS 裁剪不同——后者基于单一路径的形状来完全显示或隐藏元素的区域——遮罩可以基于遮罩图像的 Alpha 透明度以及(可选的)亮度,实现更细微的透明度和混合效果。
本指南介绍了遮罩的概念、各种遮罩图像类型,以及遮罩的亮度和 Alpha 透明度如何影响元素被遮罩(可见)的部分,以及被裁剪(或隐藏)的部分。
CSS 中的遮罩是什么?
在 CSS 中,遮罩可用于定义元素的可见区域和隐藏区域。由一个或多个 mask-image
源定义的遮罩层,决定了元素哪些区域应该可见以及其不透明度。
备注: 可以使用 mask
简写属性设置多个 CSS 遮罩属性值。
对于 alpha
遮罩,被遮罩元素的不透明度与应用的遮罩的不透明度相匹配。在 CSS 中,遮罩与化装舞会面具的作用相反,后者是在面具不透明的地方隐藏脸部。而在 CSS 中,元素中其遮罩完全不透明的区域将是完全不透明且可见的。在遮罩完全透明的地方,元素将完全隐藏。被部分不透明遮罩区域遮罩的元素区域将是部分不透明的,与其遮罩的不透明度相匹配。
对于 Alpha 遮罩,遮罩的颜色无关紧要,只有遮罩的不透明度才重要。而对于亮度遮罩,在确定被遮罩元素的不透明度时,会考虑遮罩颜色的亮度。颜色越亮、越不透明,元素就越不透明。颜色越暗、越透明,遮罩的不透明度就越低。
遮罩可以使用 CSS 渐变、栅格图像(如 PNG)和 SVG <mask>
元素来定义。在本指南中,我们将在讨论不透明度和透明度、亮度以及遮罩与 CSS 裁剪时,介绍各种遮罩图像类型。
每个遮罩层都包含一个 mask-image
,该图像相对于一个原点框进行定位。遮罩图像可以被调整大小、重复和裁剪。在声明了多个遮罩图像的情况下,可以设置遮罩层的合成或组合方式。这些内容将在遮罩属性指南中讨论。
备注: 所有示例都将使用以下图像作为应用遮罩的基础元素。

不透明度与透明度
对于 Alpha 遮罩,元素的可见区域由应用于其上的遮罩的 Alpha 透明度定义。在遮罩完全不透明的地方,元素将是可见的。在遮罩完全透明的每个像素点,元素也将完全隐藏。被遮罩的部分不透明区域遮罩的元素区域将是部分不透明的,与其所应用的遮罩的不透明度相匹配。
使用渐变
为了说明这一点,我们来看一个使用 conic-gradient()
作为 mask-image
的示例。CSS 渐变,包括锥形渐变,可用于在可见和隐藏区域之间创建平滑过渡。
在这种情况下,遮罩的右上角是完全不透明的,左上象限是完全透明的,下半部分则在不透明和透明之间平滑过渡。
.applied-mask {
mask-image: conic-gradient(black 90deg, transparent 270deg);
}
.mask-source {
background: conic-gradient(black 90deg, transparent 270deg);
}
请注意,应用了遮罩的元素的右上角是完全可见的,左上角被隐藏,下半部分则从可见平滑过渡到透明,这反映了所应用的遮罩图像的可见性。
对于 Alpha 遮罩,遮罩的颜色不重要,只有透明度才重要。在这个示例中,我们有一个条纹渐变,包含完全不透明的红色、半透明的红色和完全透明的条纹。
.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 图像。该图像包含一个带有透明背景的彩色心形。
.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
——会根据具体值将模式设置为 alpha
或 luminance
。对于除 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
。
.applied-mask {
mask-mode: luminance;
}
当对与前一个示例相同的遮罩应用 mask-mode: luminance
时,元素中遮罩颜色最亮的区域更不透明,而较暗的区域则不那么不透明。
亮度遮罩的不透明度由 RGB 颜色的 R
、G
、B
和 A
值通过以下公式确定:
((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
我们将通过在一个包含 rebeccapurple
、white
和 black
的线性渐变中,添加不透明度为 27.234%
的白色(rgb(100% 100% 100%)
,其亮度为 100%
)来演示这一点,然后用这个渐变来遮罩我们的图像。这个白色会解析为相同的不透明度值。
((0.2125 * 1) + (0.7154 * 1) + (0.0721 * 1)) * 0.27134 = 0.27134
.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-mode
的 luminance
值会在遮罩为黑色(没有亮度)的地方裁剪元素,在遮罩为不透明白色(100% 亮度)的地方完全不透明,其他区域则根据遮罩区域的亮度呈现半透明状态。
在这个示例中,我们有一个白色月亮悬挂在黑色夜空中的图像。
.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,以便你可以查看遮罩和裁剪路径的源。
<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" />
.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 透明度与遮罩相关,但与裁剪无关。遮罩可用于控制元素的不透明度,而裁剪则显示裁剪路径内的所有内容,并完全隐藏裁剪路径外的元素部分。被裁剪的区域是完全不可见的,而被遮罩的区域可以是部分或完全可见的。
如果你只需要形状,裁剪可能就足够了。但如果你需要渐变、可变不透明度,甚至控制位置和大小(我们将在另一篇指南中讨论),那么遮罩更合适。