用 CSS Mask + background-color 给图标「换色」

在做小程序或 H5 时,经常遇到这种需求:

同一套图标,在不同场景要显示成白色、品牌色、灰色......

常见做法有:导出多套 PNG、用 iconfont、filter 染色,或者本文要说的------mask + background-color

它看起来像「改了图片颜色」,本质其实是:用图片当镂空模板,颜色来自背景色

1. 一句话描述

Mask 管形状,background-color 管颜色。

  • mask / mask-image:决定「哪里看得见」
  • background-color:决定「看得见的地方是什么颜色」

图片里原来的 fill / 色值,不会作为最终显示色

2. 最小可运行示例

html 复制代码
<view class="icon"></view>
css 复制代码
.icon {
  width: 32px;
  height: 32px;
  background-color: #1a4d85; /* 最终看到的颜色 */
  -webkit-mask: url('/static/icons/question.svg') no-repeat center / contain;
  mask: url('/static/icons/question.svg') no-repeat center / contain;
}

效果:页面上出现一个「问号形状」的深蓝图标。

换颜色?只改一行:

css 复制代码
.icon {
  background-color: #fff; /* 白 */
  /* 或 #999 / red / currentColor ... */
}

同一张 SVG/PNG,可以设置为任意单色。

3. Mask 属性的作用

可以把元素想成一块纯色木板,把图标 SVG/PNG 想成一张剪纸:

  1. 先铺一层 background-color
  2. 再用图标当「模板」盖上去
  3. 模板有图形的地方:背景色透出来
  4. 模板透明的地方:背景色被裁掉

所以:

你以为的 实际发生的
在给图片重新上色 在给「背景色」按图片形状开窗
SVG 的 #245DEE 会显示出来 通常不会;它只影响遮罩轮廓
不写 background 也能显示原色 不会;没可裁切的内容时,往往什么都看不见

这也解释了:为什么图标最好是实心剪影(有形状、其余透明)。复杂的彩色插画并不适合当单色 mask。

4. 为什么要同时写 -webkit-maskmask

css 复制代码
-webkit-mask: url(...) no-repeat center / contain;
mask: url(...) no-repeat center / contain;
写法 主要覆盖
-webkit-mask Safari、iOS WebView、多数微信小程序真机环境、Chromium 系
mask 标准属性:Firefox,以及较新的浏览器

小程序 / iOS 场景里,-webkit-mask 往往更关键;两边都写是兼容写法:谁认谁生效。

博客里也常见拆开写:mask-image + -webkit-mask-image

简写 mask / -webkit-mask 可以一次带上 no-repeat center / contain,更省事。

5. 如果不设置 background-color 会怎样?

不会自动变成 SVG 自带的颜色。

  • background-color → 按遮罩形状露出该颜色
  • 没有背景(也没有别的可见内容)→ 裁切的是「空」,图标通常不可见

如果目标就是「原样显示图片颜色」,请直接用 <image> / <img>,别用 mask。

6. 和 filter: drop-shadow 染色对比

另一种常见「单色图标」套路是:

css 复制代码
.icon-wrap {
  overflow: hidden;
  color: #fff;
}
.icon-wrap image {
  transform: translateY(-32rpx);
  filter: drop-shadow(0 32rpx 0 currentColor);
}

思路是:藏起原图,用投影「伪造」一个有色图标。

方案 优点 注意点
mask + background 语义清晰;换色只改颜色值;真机兼容相对稳 图标需是剪影;依赖 mask 支持
drop-shadow 投影 老项目里常见 依赖 filter;原图被藏起,一旦 filter 失效就「彻底空白」;SVG 缺 fill 时真机可能无投影源

两者目标类似,但 mask 更接近「换色」本身,也少踩「影子画不出来」这类坑。

7. 实际使用场景建议(可直接当 checklist)

  1. 图标资源:优先单色剪影 SVG/PNG,透明底
  2. 尺寸:给容器写死宽高(小程序里尤其重要)
  3. 颜色 :用 background-colorcurrentColor(方便跟文字色联动)
  4. 兼容-webkit-maskmask 成对写
  5. 定位no-repeat center / contain,避免裁切变形
  6. 别指望 mask 还原多彩色原图------它擅长的是单色图标系统

进阶一点可以用渐变背景,做出双色/渐变图标:

css 复制代码
.icon {
  background: linear-gradient(135deg, #4e6ef2, #8b5cf6);
  -webkit-mask: url('/static/icons/star.svg') no-repeat center / contain;
  mask: url('/static/icons/star.svg') no-repeat center / contain;
}

8. 一句话总结

mask 不是「给图片调色板」,而是:

借图标的形状,给背景色打孔。

所以博客里说的「通过修改 background-color 得到各种颜色的图案」,完全成立------前提是你接受:最终色来自背景,形状来自图片


参考

相关推荐
幼儿园技术家2 小时前
原来不用发版也可以做到版本更新
前端·js or ts
亦暖筑序2 小时前
AgentScope-Java 入门:完善 Vue 前端、发布 GitHub,并规划下一步
java·前端·vue.js
北斗落凡尘2 小时前
Vue面试题
前端
程序员黑豆3 小时前
鸿蒙应用开发之父子组件传参:@Param、@Event、@Once 装饰器详解与实战
前端·harmonyos
ClouGence3 小时前
一个人录好的测试用例,团队怎么一起用?
前端·测试
无限压榨切图仔3 小时前
从 Claude Code 切到 Codex:我用 Agent、Skills、MCP 做完了一个内容运营工具
前端·后端
濮水大叔3 小时前
NestJS 与 CabloyJS 的 env/config 架构对比:从环境变量到实例级配置
前端·node.js·nestjs
成都渲染101云渲染66663 小时前
Blender渲染时,纯CPU渲染的设置教程
前端·javascript·blender
董员外3 小时前
RAG 系统进化论(一):纵览 RAG 的发展历程
前端·人工智能·后端