We read every piece of feedback, and take your input very seriously.
To see all available qualifiers, see our documentation.
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
图标变色是一个常见需求,比如根据用户心情自动切换皮肤。🐶
本文以微信小程序为例。
第一反应是用 svg + currentColor。比如:
<div class="box"> <svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 15.7715 20.4004" width="15.7715" height="20.4004" > <path d="M11.3965 4.66797C9.94141 4.66797 8.75 5.55664 7.98828 5.55664C7.17773 5.55664 6.12305 4.66797 4.85352 4.66797C2.44141 4.66797 0 6.71875 0 10.4688C0 12.8125 0.898438 15.2832 2.02148 16.875C2.97852 18.2227 3.81836 19.3066 5.0293 19.3066C6.2207 19.3066 6.74805 18.5352 8.23242 18.5352C9.73633 18.5352 10.0781 19.3066 11.3965 19.3066C12.7051 19.3066 13.5742 18.1055 14.4043 16.9238C15.3223 15.5664 15.7129 14.248 15.7227 14.1797C15.6445 14.1602 13.1445 13.1348 13.1445 10.2734C13.1445 7.79297 15.1074 6.67969 15.2246 6.5918C13.9258 4.72656 11.9434 4.66797 11.3965 4.66797ZM10.7129 3.08594C11.3086 2.36328 11.7285 1.37695 11.7285 0.380859C11.7285 0.244141 11.7188 0.107422 11.6992 0C10.7227 0.0390625 9.55078 0.644531 8.85742 1.46484C8.30078 2.08984 7.79297 3.08594 7.79297 4.08203C7.79297 4.23828 7.82227 4.38477 7.83203 4.43359C7.89062 4.44336 7.98828 4.46289 8.0957 4.46289C8.96484 4.46289 10.0586 3.87695 10.7129 3.08594Z" /> </svg> </div>
.box { color: #0066cc; } .box svg { fill: currentColor; }
CodePen
动态修改父元素的颜色就能变色。
然,微信小程序不支持 <svg /> 标签。
<svg />
小程序 image 标签是支持 svg 格式图片的(一些限制)。
注意,将 fill="currentColor" 的 svg 图片放到 image 标签,该图片是无法读取其本身或父级颜色的,因此呈现会默认的黑色。
fill="currentColor"
要做一些转换处理:
function genColorfulSvg(url, color) { const localFilePath = await downloadSvgFile(url) const svgSourceStr = await readSvgFile(localFilePath) const colorfulSvgSourceStr = replaceSvgFillColor(svgSourceStr, color) const colorfulSvgBase64 = genSvgBase64(colorfulSvgSourceStr) return colorfulSvgBase64 }
// 下载图片 async function downloadSvgFile(url) { return new Promise((resolve, reject) => { wx.downloadFile({ url, success: res => { if (res.statusCode === 200) return resolve(res.tempFilePath) reject(new Error('download file fail')) }, fail: err => reject(err), }) }) }
// 读取 svg 文件 async function readSvgFile(localFilePath) { return new Promise((resolve, reject) => { const fs = wx.getFileSystemManager() fs.readFile({ filePath: localFilePath, encoding: 'utf-8', position: 0, success: res => { const sourceFile = res.data resolve(sourceFile) }, fail: err => reject(err), }) }) }
// 替换 fill 属性 function replaceSvgFillColor(svgSourceStr, newFillColor) { // 匹配 fill="..." 中的非 none 值(按需调整) return svgSourceStr.replace(/fill="(?!none)[^"]*"/g, `fill="${newFillColor}"`) }
// 生成 base64 function genSvgBase64(svgSourceStr) { return `data:image/svg+xml;base64,${base64Encode(svgSourceStr)}` }
示例中 base64Encode() 来自掘金社区(原文),找其他也行。
base64Encode()
将以上封装成一个自定义组件,比如:
<colorful-image color="xxx" src="xxx" />
可以在 Component 的 lifetimes.attached() 时机执行,为了避免重复下载转换,可以用 Map 将 base64 存起来。
lifetimes.attached()
(具体实现就不写了)
这个方案的缺点是要额外的读取、替换、转换,性能不太好。
这种方案使用 CSS Filter 函数 drop-shadow,它通常用于生成阴影效果。
常规阴影是位于图片下方的,借助 transform 进行一些位移操作,思路如下:
过程中遇到了一些坑,先以 Web 为例:
<img class="icon" src="../../images/star.svg" />
.icon { width: 100rpx; height: 100rpx; filter: drop-shadow(200rpx 0 0 #1f883d); transform: translateX(-200rpx); }
▲ 黄色为原图,绿色为投影
接着,添加一个父级元素,并设为 overflow: hidden。
overflow: hidden
但是 drop-shadow 有一个表现特性:
在 Chrome 浏览器下,如果一个元素的主体部分,无论以何种方式,只要在页面中不可见,其 drop-shadow 是不可见的;实体部分哪怕有 1px 可见,则 drop-shadow 完全可见。(源自张鑫旭大佬的博客)
亲测 Safari 18.1 也有相同表现,不显示阴影,而 Firefox 132 是符合预期的。
文章里提到一个解决方案是用透明边框,使元素总在屏幕可视区域内。
调整下代码:
+ <view class="icon-box"> <img class="icon" src="../../images/star.svg" /> + </view>
+ .icon-box { + width: 100rpx; + height: 100rpx; + overflow: hidden; + } .icon { width: 100rpx; height: 100rpx; filter: drop-shadow(200rpx 0 0 #1f883d); transform: translateX(-200rpx); + border-right: 1000px solid transparent; }
这样的话,在 Chrome 下是符合预期了(黄色隐藏,绿色显示),然而 Safari 下阴影并没有显示出来。
看起来有点类似这个《深究 Safari 下 border-radius & overflow 不生效的问题》,给 icon 添加 transform: translateZ(0) 或 will-change: transform 等解决,但 Chrome 又不能加,可用 CSS Media Query 区分(小程序亲测有用)。
transform: translateZ(0)
will-change: transform
如果使用 wx.getSystemInfo().platform 来区分设备,要注意开发工具、PC 端、Android 端与 Chrome 表现一致,不能加 will-change: transform 等处理,否则不显示。
wx.getSystemInfo().platform
比如:
@supports (-webkit-hyphens: none) { /* Safari for macOS or iOS browsers */ .icon { will-change: transform; } }
测试结果,Chrome、Safari、Firefox 和 iOS 浏览器都符合预期了。
⚠️ 注意,本打算给 border-right 给一个足够大的边框宽度,以兼容各种尺寸的图标,但发现过大时在 Safari 下不会呈现阴影,暂未发现有啥规律。但通常图标不会很大,如小程序端可以设置一屏大小 750rpx 的边框宽度。
border-right
小程序组件:
<colorful-image image-class="xxx" color="xxx" src="xxx" />
Component({ externalClasses: ['image-class'], properties: { // Required src: { type: String, value: '', }, color: { type: String, value: 'transparent', }, }, })
{ "component": true, "usingComponents": {} }
<view class="image-class inner-image-box"> <image class="inner-image" style="filter: drop-shadow(760rpx 0 0 {{ color }})" src="{{ src }}" /> </view>
.inner-image-box { overflow: hidden; } .inner-image { box-sizing: content-box; display: block; width: 100%; height: 100%; border-right: 100000px solid transparent; transform: translateX(-760rpx); } @supports (-webkit-hyphens: none) { .inner-image { will-change: transform; } }
小程序代码片段
注意:Skyline 渲染模式暂不支持。
The text was updated successfully, but these errors were encountered:
No branches or pull requests
图标变色是一个常见需求,比如根据用户心情自动切换皮肤。🐶
本文以微信小程序为例。
使用 svg 标签
第一反应是用 svg + currentColor。比如:
CodePen
动态修改父元素的颜色就能变色。
然,微信小程序不支持
<svg />
标签。使用 image 标签 + base64
小程序 image 标签是支持 svg 格式图片的(一些限制)。
注意,将
fill="currentColor"
的 svg 图片放到 image 标签,该图片是无法读取其本身或父级颜色的,因此呈现会默认的黑色。要做一些转换处理:
将以上封装成一个自定义组件,比如:
可以在 Component 的
lifetimes.attached()
时机执行,为了避免重复下载转换,可以用 Map 将 base64 存起来。(具体实现就不写了)
这个方案的缺点是要额外的读取、替换、转换,性能不太好。
使用 image 标签 + CSS Filter
这种方案使用 CSS Filter 函数 drop-shadow,它通常用于生成阴影效果。
常规阴影是位于图片下方的,借助 transform 进行一些位移操作,思路如下:
过程中遇到了一些坑,先以 Web 为例:
▲ 黄色为原图,绿色为投影
接着,添加一个父级元素,并设为
overflow: hidden
。但是 drop-shadow 有一个表现特性:
亲测 Safari 18.1 也有相同表现,不显示阴影,而 Firefox 132 是符合预期的。
文章里提到一个解决方案是用透明边框,使元素总在屏幕可视区域内。
调整下代码:
这样的话,在 Chrome 下是符合预期了(黄色隐藏,绿色显示),然而 Safari 下阴影并没有显示出来。
看起来有点类似这个《深究 Safari 下 border-radius & overflow 不生效的问题》,给 icon 添加
transform: translateZ(0)
或will-change: transform
等解决,但 Chrome 又不能加,可用 CSS Media Query 区分(小程序亲测有用)。比如:
测试结果,Chrome、Safari、Firefox 和 iOS 浏览器都符合预期了。
小程序组件:
小程序代码片段
注意:Skyline 渲染模式暂不支持。
The text was updated successfully, but these errors were encountered: