用 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-mask 和 mask

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-color 或 currentColor(方便跟文字色联动)
  4. 兼容 :-webkit-mask 与 mask 成对写
  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 得到各种颜色的图案」,完全成立------前提是你接受:最终色来自背景,形状来自图片。


参考

相关推荐
LucianaiB1 小时前
【元宝搜索开发者征文大赛】AI时代,学得慢就不用学?用元宝搜索打造一个教程保质期,确保每一个教程符合你的现状
前端·人工智能·ai编程
excel2 小时前
JavaScript 对象池:减少高频对象创建与 GC 压力
前端
陈随易3 小时前
纯 Go 写的 JS 引擎,跑分超过了 C 语言的 QuickJS
前端·后端·程序员
子兮曰5 小时前
Chrome 权限元素实战:144 与 151 的渐进增强写法
前端·javascript·chrome
IT_陈寒5 小时前
Redis连接池泄漏那次,我差点把生产环境搞崩了
前端·人工智能·后端
强尼银手5 小时前
我花一天做了个格斗游戏原型,判定全对,然后被「美术」劝退了
前端·javascript
石小石Orz5 小时前
AI 正在蚕食我们的编码能力
前端·后端·算法
陈随易6 小时前
专注飞机 FocusFlight,两个人把番茄钟做成了一趟航班
前端·后端·程序员
总要学点什么7 小时前
第98篇:三维特效-材质线效果——给一条线换十种材质,虚线的图案只填了一半
开发语言·前端·javascript·ecmascript·gis·webgl·材质
10share7 小时前
基于大模型的 i18n 方案:把中文原文当 Key,轻松搞定多语言维护
前端·vue.js