CSS 中的遮罩属性 mask/masks

最近有点忙,于是又停更了一个月😂,前两天在 V2 上看到 一个帖子,需求是要做一个镂空遮罩。

回答的过程中想起来有一个 mask 属性来着,好像可以试试,但是快下班了,也没有找到合适的遮罩图,也不想打开PS自己画一个再传图床,就还是用的 clip-path 做了一个 CodePen Demo
这两天稍微有点时间,可以考虑研究一下这个遮罩属性,以及能否搭配 filter:drop-shadow() 使用。

先来看一下 mask 属性的介绍

mask 属性允许使用者通过遮罩或者裁切特定区域的图片的方式来隐藏一个元素的部分或者全部可见区域。
mask: [mask-image] [mask-repeat] [mask-position] / [ mask-size];

最简单的直接使用 mask 然后配置一下 url 就可以应用上遮罩层了。

<!DOCTYPE html>
<html>
<head>
  <title>Document</title>
  <style>
    .mask{
      width:100vw;
      height:100vh;
      -webkit-mask: url(./mask.png); 
      mask: url(./mask.png); 
      display: block;
    }
  </style>
</head>
<body>
  <img class="mask" src="./picture.jpg">
</body>
</html>

效果截图 👇

demo-直接使用mask

可以看到需要设置 -webkit- 私有前缀(Firefox不需要),
然后遮罩图片的话,我尝试以下两张图,A:黑白JPG图片;B:黑色圆形透明底PNG图片。
遮罩图片预览

只有透明底的PNG图片才可以应用上遮罩,图片A尝试多次都不可以应用上
【测试时间:2021年7月9日】,因为是 chrome 通过遮罩层的 alpha (透明)通道来实现遮罩功能的,并不是通过亮度和颜色差值

❓ 那么 SVG 图形呢?

<svg xmlns="http://www.w3.org/2000/svg" version="1.1">
  <circle cx="400" cy="400" r="300" fill="black"/>
</svg>

也可以直接使用,截图就不加了,和上方的差不多只是没有 repeat,可能是因为绘制得太大了😂
- 追加:因为 <svg> 上没有设置 widthheight 属性😐,设置上就会 repeat

然后是8个具体属性

  1. mask-image: 遮罩图像;
  2. mask-mode: 遮罩图像的模式;
  3. mask-repeat: 遮罩图像的重复方式(同bg-repeat);
  4. mask-position: 遮罩图像的定位(同bg-position);
  5. mask-clip: 遮罩图像应用区域(同bg-clip);
  6. mask-origin: 遮罩图像应用原点(同bg-origin);
  7. mask-size: 遮罩图像尺寸大小(同bg-size);
  8. mask-composite: 遮罩图像的叠加方式。

基本上可以与 background 属性一样来理解并且语法相通,只有两个属性需要单独去尝试

  • mask-mode - 遮罩图像的模式;
  • mask-composite - 遮罩图像的叠加方式。

因为只有 firefox 支持 mask-modemask-composite 属性,所以后边的示例都是在 firefox 下尝试的

mask-mode和mask-composite兼容性_2021年7月12日
Can I use results 2021年7月12日

🧷 1. 按顺序先来讲 mask-mode 属性

直接读文档 mask-mode - CSS | MDN,可以看到:

  • alpha: 使用遮罩层图像的透明度(alpha)值作为掩码值;
  • luminance: 遮罩层图像的亮度值应用作掩码值;
  • match-source: 资源的类型自动采用合适的遮罩模式。
    • 如果 mask-image 属性是 <mask-source> 类型,遮罩层的亮度值会被作为掩码值;
    • 如果它是类型 <image>,遮罩层的 alpha 值作为掩码值。

👇 MDN示例CodePen 中打开 (记得使用FireFox打开)
示例图 mask-mode

嗯???为什么少了 mask-mode:match-source ?
因为他只是 auto 的意思…

例如,如果我们的遮罩使用的是SVG中的<defs>中的<mask>元素,则此时的mask-mode属性的计算值是luminance,表示基于亮度遮罩。如果是其他场景,则计算值是alpha,表示基于透明度遮罩。
张鑫旭 - 鑫空间


🧷 2. mask-composite 属性

mask-composite 表示当同时使用多个图片进行遮罩时候的混合方式。

mask-composite: add;         // 叠加(遮罩累加/并集)
mask-composite: subtract;    // 相减(遮罩相减/补集)
mask-composite: intersect;   // 相交(遮罩重叠部分/交集)
mask-composite: exclude;     // 排除(非重叠区域的合并/对称差)

还是使用MDN上的实例演示哈,CodePen 中打开
示例图 mask-composite

因为实例的star.svg是半透明的,所以mask-composite: subtractmask-composite: exclude 看起来会有一些疑问,从现象来描述是:

  • 相减:与后面遮罩图片重合的地方不显示,前一个遮罩结果之上继续应用后一个遮罩,遮罩互相应用。意味着遮罩图片越多,遮罩的区域越小。
  • 排除:与后面遮罩图片重合的地方排除,也就是非重叠区域的合并,反向的相交 => 对称差

使用没有透明度的遮罩示例来对照以下应用后的结果
示例图 mask-composite


尾声

可以看到就算是2021年了,浏览器对于 mask 属性的实现还是有一些争议,chrome 还需要加私有前缀(-webkit-)才可以应用上,而且部分属性也不支持,吐槽一下已经 CR 5年了…..

虽然可以用,但是毕竟是一个处于 候选标准(CR)阶段 的属性,MDN也给予了提示,尽量控制适用范围。

这是一个实验中的功能
此功能某些浏览器尚在开发中,请参考浏览器兼容性表格以得到在不同浏览器中适合使用的前缀。由于该功能对应的标准文档可能被重新修订,所以在未来版本的浏览器中该功能的语法和行为可能随之改变。

然后文章开头所提到的提问,通过 mask 属性的实现:

clip-path 一样,使用了 mask 属性之后会对所有内容进行裁剪,所以 filter drop-shadow() 应用的投影会被隐藏,所以需要在父级应用投影效果。

追加

本来写完上边之后就结束了这篇笔记,但是看到了一个使用 element(#id) 作为遮罩层的例子,例如:

<div class="wrap">
  <img id="mask" src="./mask.png">
  <img id="picture" src="./picture.jpg">
</div>
<style>
#picture {
  width: 300px;
  mask-image: -moz-element(#mask);
}
#mask {
  display: none;
}
</style>

可以使用文字等 DOM元素 作为遮罩元素,但如果不想要遮罩层展示的话,还需要隐藏遮罩的 DOM,而且这个特性只有 FireFox 才支持 Can I use,大概了解一下就好了。

mask - CSS(层叠样式表) | MDN
CSS Masks | Can I use
CSS Masking Module Level 1 - W3C Candidate Recommendation

客栈说书:CSS遮罩CSS3 mask/masks详细介绍 - 张鑫旭
mask-mode | CSS-Tricks
css:mask浅析 - SegmentFault
JELLY | CSS3 Mask 安利报告 - JELLY

element() - CSS: Cascading Style Sheets | MDN
“-moz-element” | Can I use
CSS Image Values and Replaced Content Module Level 4 - W3C Working Draft