用 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 得到各种颜色的图案」,完全成立------前提是你接受:最终色来自背景,形状来自图片


参考

相关推荐
绿岛之北1 分钟前
Electron 安全第三章:URL 加载与 WebView
前端·electron
sunoo-2292 分钟前
C 语言文件 IO 全攻略:从基础函数到实战踩坑(BMP 读取 + 词典查询)
linux·c语言·前端·笔记·vscode·学习
Shinner欣儿4 分钟前
React18 并发渲染小记
前端
用户9210802628612 分钟前
AI 对话里的消息时间线分页:上滑加载更多历史记忆的实现与坑点
前端
PedroQue9915 分钟前
Vue-Router 2.4.0 新增可控重定向功能
前端·uni-app
阿萨德528号37 分钟前
npm 包发布实战指南:从零发布、更新迭代到版本治理
前端·npm·策略模式
IT_陈寒1 小时前
Vue的v-for为啥把我的渲染顺序搞乱套了?
前端·人工智能·后端
sir.山1 小时前
在 Vue 3 项目中使用 Mock 数据
前端·vue.js·vue3·vite
李剑一2 小时前
华为新上Pura X View阔直板手机,特殊屏幕比例设备下,前端应该怎么去适配更完美?送你一套完整工具代码
前端
0xBADCODE2 小时前
Flask SSTI读SECRET_KEY+伪造Session:税务系统渗透全流程
前端·后端·python·安全·web安全·网络安全·flask