“蒙”出花样!用 CSS Mask 实现丝滑视觉魔法

引言

在前端开发中, CSS 不再仅仅用于布局和样式修饰, 越来越多的高级视觉效果也可以通过纯 CSS 实现, 其中就包括令人惊艳的 蒙版 效果。mask-image 作为 CSS 中用于控制元素可见区域的强大属性, 能够帮助开发者实现类似 Photoshop 中的遮罩操作, 无需借助复杂的图像处理。无论是实现渐隐文字、柔和的图像遮罩, 还是动态的 手电筒 追光效果, mask-image 都提供了灵活而优雅的解决方案。本文将深入介绍 mask-image 的基本用法、支持的各种类型(如渐变、SVG)、配套属性 (如 mask-mode), 并结合多个实战示例, 带你全面掌握 CSS 蒙版的使用技巧。

一、基本语法

mask-imageCSS 中用来定义蒙版效果的一个属性。它可以根据一个图像或渐变、控制 元素 的可见区域, 实现类似 Photoshop 中的蒙版功能。

如下代码所示, background-image 所支持的 mask-image 都可以适用, 并且类似 background 系列属性, mask-* 中也都有对应的属性, 用于设置蒙版图参数

html 复制代码
<div className="wrapper" />
<style>
.wrapper {
  width: 500px;
  height: 500px;

  // 设置背景图
  background-image: url('./bg.png');
  background-size: cover;
  background-position: center;
  background-repeat: no-repeat;

  // 设置蒙版
  mask-image: url('./mask_alpha.png');
  mask-size: 100%;
  mask-repeat: no-repeat;
  mask-position: center;
}
</style>

如下图是上面代码效果, 默认情况下 遮罩图像的 Alpha(透明度) 的值将会作用于 元素:

  • 透明度为 100% 的区域, 则会完全展示对应元素内容
  • 透明度为 0% 的区域, 则会完全隐藏对应元素内容

二、 使用渐变

background-image 我们这边也是可以使用渐变的:

html 复制代码
<div className="wrapper" />
<style>
.wrapper {
  width: 500px;
  height: 500px;
  // 设置背景图
  background-image: url('./bg.png');
  background-size: cover;
  background-position: center;
  background-repeat: no-repeat;
  // 设置渐变蒙版
  mask-image: linear-gradient(to bottom, rgb(255 0 0 / 0%) 0%, rgb(255 0 0 / 100%) 100%);
}
</style>

如上代码, mask-image 设置为一个渐变效果, 整个渐变只是在透明度上发生变化, 而最终效果如下:

三、 使用 SVG

注意不同于 background-image, mask-image 还可以设置为某个 svg 上的 <mask/>。如下代码所示我们在 svg 中定义了一个 <mask/> 并在 mask-image 中通过 url('#mask') 方式进行了引用。

html 复制代码
<div className="wrapper" />
<svg viewBox="-10 -10 300 300">
  <mask id="mask">
    <ellipse
      cx="50%"
      cy="50%"
      rx="25%"
      ry="25%"
      fill="white"
    />
  </mask>
</svg>

<style>
.wrapper {
  width: 500px;
  height: 500px;
  // 设置背景图
  background-image: url('./bg.png');
  background-size: cover;
  background-position: center;
  background-repeat: no-repeat;
  // 设置渐变蒙版
  mask-image: url('#mask');
}
</style>

而最终的效果如下:

四、可应用于任意元素

我们先将上文用到的蒙版图片转为 base64 数据

下面我们在掘金上进行尝试, 在控制台直接在 body 上设置蒙版:

上面例子是为了说明蒙版可作用于任何元素, 可直接将任意元素部分

五、 mask-mode: 设置蒙版规则

上文我们提到, 默认情况下 遮罩图像的 Alpha(透明度) 的值将会作用于 元素:

  • 透明度为 100% 的区域, 则会完全展示对应元素内容
  • 透明度为 0% 的区域, 则会完全隐藏对应元素内容

之所以如此是因为 mask-mode 默认值为 match-source 即模版作用规则由源决定, 这里就两种情况:

  • 如果 mask-image 引用的是 SVG 中的 <mask>, 则使用其 mask-type 属性值(如果存在)。如果未明确设置, 则此值默认为 Alpha 模式。
  • 如果蒙版图片的源是 <image><gradient>, 则使用蒙版图像的 Alpha 值。

那么除了 Alpha 模式之外还有其他的模式吗? 有的, 那就是 luminance(亮度), 即根据模版图片不同的亮度来控制元素的显隐:

  • 蒙版图片中黑色区域, 其对应位置元素完全透明(不可见)
  • 蒙版图片中白色区域, 其对应位置元素完全不透明(可见)
  • 蒙版图片中灰色区域, 其对应位置元素半透明
  • 蒙版图片中越白部分, 其对应位置元素透明度越高, 反之越黑则透明度越低

而这里我们可通过 mask-mode: <alpha | luminance | match-source>, 来设置模版作用模式, 如下代码所示

html 复制代码
<div className="wrapper" />
<style>
  .wrapper {
    width: 500px;
    height: 300px;
    background-image: url("./bg.png");
    background-size: cover;
    background-position: center;
    background-repeat: no-repeat;
    // 设置蒙版模式为亮度模式
    mask-mode: luminance;
    // 蒙版图片为渐变, 即白 => 黑
    mask-image: linear-gradient(to bottom, #fff, #000);
    mask-size: 100%;
    mask-repeat: no-repeat;
    mask-position: center;
  }
</style>

最终效果如下, 蒙版白色部分元素完全可见, 黑色部分元素不可见, 中间灰色过渡对应的就是元素可见度的过渡

六、 DEMO: 渐变隐藏

我们要实现这么一个效果:

  • 我们有个容器, 容器高度是自适应
  • 同时容器最大高度默认为 200px 超出部分需要隐藏
  • 但是如果直接隐藏容器底部过渡就特别生硬
  • 如下代码所示:
html 复制代码
<div className="container">
  <p>
    我经常遇到两种候选人。一种是一听算法题, 就两手一摊, 表情痛苦, 说"哥, 我天天写业务, 真没准备这个"。另一种呢, 正好相反, 题目一出, 眼睛一亮, 不出三十秒, 就把 LeetCode 上背得滚瓜烂熟的最优解, 一字不差地敲了出来, 然后一脸期待地看着我。
    说实话, 这两种, 都不是我最想看到的。
  </p>
  <p>
    这就引出了一个很多候选人都想问, 但不敢问的问题:"你们这些面试官, 到底怎么想的?你们明知道我们前端平时工作中, 99%的时间都用不上这些, 为什么非要折磨我们?"
    今天, 我就想站在桌子对面, 跟大伙掏心窝子地聊聊, 我们问算法题, 到底图个啥。
  </p>
</div>
<style>
  .container {
    width: 400px;
    color: #999;
    max-height: 200px;
    overflow: hidden;
  }
</style>

最后效果如下, 容器底部文字硬生生的被切断了

而更好的效果应该是有个完美的过渡效果, 这里我们就可以使用蒙版来处理: 如下代码所示, 蒙版是一个渐变, 从下到上, 渐变透明度从 0 ~ 100 一个过渡

diff 复制代码
<style>
  .container {
    width: 400px;
    color: #999;
    max-height: 200px;
    overflow: hidden;
+   mask-image: linear-gradient(to top, rgb(0 0 0 / 0%), rgb(0 0 0 / 100%) 40px);
  }
</style>

而最终效果如下: 整个过渡还是很丝滑的

然而有些站点为了实现上述过渡效果, 简单粗暴的在容器底部覆盖了一层渐变背景图! 而如此实在不够优雅, 如果页面背景复杂的话就完全没有效果!

但是用我们的方式肯定就能应付上面这情况了。

下面看另一个例子, 有代码如下:

html 复制代码
<div className="container">
  我经常遇到两种候选人。一种是一听算法题, 就两手一摊, 表情痛苦, 说"哥, 我天天写业务, 真没准备这个"。另一种呢,
  正好相反, 题目一出, 眼睛一亮, 不出三十秒不出三十秒, 就把 LeetCode 上背得滚瓜烂熟的最优解, 一字不差地敲了出来,
  然后一脸期待地看着我。
</div>
<style>
  .container {
    width: 400px;
    color: #999;
    max-height: 200px;
    overflow: hidden;
  }
</style>

效果如下, 但是我们希望在第二行行末, 有个渐隐的效果:

下面我们直接改代码:

html 复制代码
<div className="container">
  我经常遇到两种候选人。一种是一听算法题, 就两手一摊, 表情痛苦, 说"哥, 我天天写业务, 真没准备这个"。另一种呢,
  正好相反, 题目一出, 眼睛一亮, 不出三十秒不出三十秒, 就把 LeetCode 上背得滚瓜烂熟的最优解, 一字不差地敲了出来,
  然后一脸期待地看着我。
</div>
<style>
.container {
  width: 600px;
  color: #999;
  line-height: 1.6em;
  max-height: 3.2em;
  overflow: hidden;
  mask-mode: luminance;
  mask-image: radial-gradient(ellipse 1000px 300px at 100% 40px, #000, #fff 10%, #fff 10%);;
}
</style>

最终效果:

而这里实际上使用了椭圆渐变来实现蒙版, 我们可以把蒙版改为背景, 来看下蒙版图片的样子:

diff 复制代码
.container {
  width: 600px;
  color: #999;
  line-height: 1.6em;
  max-height: 3.2em;
  overflow: hidden;
+ background-image: radial-gradient(ellipse 1000px 300px at 100% 40px, #000, red 10%, red 10%);;
}

如下图所示:

七、 DEMO: 手电筒

你也许看到过下图类似的一个效果, 这看起来也许很唬人, 但是实际上了解了蒙版的概念后, 实现这么一个效果还是很简单的:

如下代码所示:

  1. 我们只需要通过 onMouseMoveonMouseLeave 来记录鼠标移动位置
  2. 并将鼠标位置存储下来, 转为 CSS 变量 --client-x 以及 --client-y
  3. 最后使用动态的 CSS 变量来渲染蒙版即可
js 复制代码
const MaskImagePage: FC = () => {
  const [client, setClient] = useState(HIDE_CLIENT);

  const handleMouseMove = useCallback((e) => {
    setClient({
      x: e.clientX,
      y: e.clientY,
    });
  }, []);

  const handleMouseLeave = useCallback(() => {
    setClient(HIDE_CLIENT);
  }, []);

  return (
    <div
      className="page"
      onMouseMove={handleMouseMove}
      onMouseLeave={handleMouseLeave}
      style={{ 
        '--client-x': `${client.x}px`, 
        '--client-y': `${client.y}px`,
      }}
    />
  );
};
css 复制代码
.page {
  width: 100vw;
  height: 100vh;
  background-image: url("./page_bg.png"); // 随便一个背景图, 这个不重要
  background-size: 100% 100%;
  mask-mode: luminance;
  mask-image: radial-gradient(circle at var(--client-x) var(--client-y), #fff, #000 100px);
}

八、DEMO: 惊艳的过渡转场

奇妙的 CSS MASK 一文中, 有这么一个效果:

而这里就使用到蒙版, 蒙版图片如下所示, 其实就是好多帧蒙版拼接出来的一个长图:

而关键代码如下, 其实就是通过控制 mask-position 控制蒙版图片的位置, 从而实现过渡的转场效果:

css 复制代码
.container {
  mask-image: url(https://i.imgur.com/AYJuRke.png);
  mask-size: 3000% 100%;
}

@keyframes maskMove {
  from {
    mask-position: 0 0;
  }
  to {
    mask-position: 100% 0;
  }
}

完整 DEMO 查看: mask 制作转场动画

九、参考文档

相关推荐
passerby606125 分钟前
完成前端时间处理的另一块版图
前端·github·web components
掘了32 分钟前
「2025 年终总结」在所有失去的人中,我最怀念我自己
前端·后端·年终总结
崔庆才丨静觅35 分钟前
实用免费的 Short URL 短链接 API 对接说明
前端
崔庆才丨静觅1 小时前
5分钟快速搭建 AI 平台并用它赚钱!
前端
崔庆才丨静觅1 小时前
比官方便宜一半以上!Midjourney API 申请及使用
前端
Moment1 小时前
富文本编辑器在 AI 时代为什么这么受欢迎
前端·javascript·后端
崔庆才丨静觅2 小时前
刷屏全网的“nano-banana”API接入指南!0.1元/张量产高清创意图,开发者必藏
前端
剪刀石头布啊2 小时前
jwt介绍
前端
爱敲代码的小鱼2 小时前
AJAX(异步交互的技术来实现从服务端中获取数据):
前端·javascript·ajax
Cobyte2 小时前
AI全栈实战:使用 Python+LangChain+Vue3 构建一个 LLM 聊天应用
前端·后端·aigc