背景
前段时间做的一个需求,菜单的图标是通过接口返回,且在不同主题下使用不同的颜色。 接到这个需求,大脑立刻想到的做法是接口配置了N套不同主题的图标。这种做法,可能会带来的一个问题是,当存在多套主题时,同一个系统就要配置多套图标。但有时候,主题与主题之间的不同点仅仅是配色。如何通过只配置一套图标,就能在不同的主题下渲染为相应的颜色,这是这篇文章的背景了。
filter
在网络上,搜索css改变svg图标颜色,最多的推荐方式是使用css的filter
,于是,我立马动起手来。
html
<span class="menu-item-wrapper">
<span id="menuSpan" class="menu-item-icon" />
</span>
css
.menu-item-wrapper{
display: inline-block;
width: 128px;
height: 128px;
overflow: hidden;
}
.menu-item-icon{
display: inline-block;
width: 128px;
height: 128px;
filter: drop-shadow(#474F5E -128px 0);
background-image: url(./home.svg);
transform: translateX(128px);
}
.menu-item-wrapper:hover .menu-item-icon{
filter: drop-shadow(red -128px 0);
}
在Chrome的效果如下:
初始状态:
鼠标hover后改变颜色:
但是在safari浏览器下,却会因为父元素overflow:hidden
导致图标无法显示。在移动端浏览器的效果,也存在兼容问题。虽然可以通过各种修修补补的方式实现目的,但让人难受,总要担心没有测试到的浏览器,显示会异常。
-webkit-mask-box-image

虽然是不标准的语法,但是,它在safari的效果,却让人满意。
html
<!DOCTYPE html>
<html>
<head>
<style>
.menu-item-wrapper{
display: inline-block;
width: 30px;
height: 30px;
overflow: hidden;
}
.menu-item-wrapper:hover .menu-item-icon{
background-color: red;
}
.menu-item-icon{
display: inline-block;
width: 30px;
height: 30px;
background-color: #474F5E;
-webkit-mask-box-image: url('./home.svg') 0 0;
}
</style>
</head>
<body>
<div>
<span id="menuSpanWrapper" class="menu-item-wrapper">
<span id="menuSpan" class="menu-item-icon" />
</span>
</div>
</body>
</html>
初始状态:

鼠标hover后改变颜色:

当然,它也不是没有坑的,它在firefox下是不支持的。
最终方案
综合了这两个样式的特点,于是,我想到了个折中的方式: Chrome、safari,使用-webkit-mask-box-image
firefox,使用filter
html
<!DOCTYPE html>
<html>
<head>
<style>
.menu-item-wrapper{
display: inline-block;
width: 128px;
height: 128px;
overflow: hidden;
}
.menu-item-wrapper:hover .menu-item-icon{
background-color: red;
}
.menu-item-icon{
display: inline-block;
width: 128px;
height: 128px;
background-color: #474F5E;
}
.menu-item-icon.firefox{
background-color: unset;
filter: drop-shadow(#474F5E -128px 0);
transform: translateX(128px);
}
.menu-item-wrapper:hover .menu-item-icon.firefox{
background-color: unset;
filter: drop-shadow(red -128px 0);
}
</style>
</head>
<body>
<div>
<span id="menuSpanWrapper" class="menu-item-wrapper">
<span id="menuSpan" class="menu-item-icon" />
</span>
</div>
</body>
<script>
const isFirefox = /firefox/i.test(navigator.userAgent)
const menuSpanEl = document.getElementById('menuSpan')
if(isFirefox) {
menuSpanEl.classList.add('firefox')
menuSpanEl.style.backgroundImage = 'url("./home.svg")'
}else{
menuSpanEl.style.webkitMaskBoxImage = 'url("./home.svg") 0 0'
}
</script>
</html>