SVG 是图标的绝佳格式。 矢量格式无论尺寸或设备如何,都显得清晰锐利 - 而且我们在使用内联矢量格式时获得了大量的设计控制权。
SVG 还为我们提供了另一个强大的功能:使用 CSS 操纵其属性的能力。 因此,我们可以进行快速简单的交互,而以前需要使用巧妙的 CSS 技巧或替换整个图像文件。
这些交互包括在悬停状态下更改颜色。 在 2019 年,这听起来像是件非常简单的事情,但实际上有几种完全有效的方法可以实现它 - 这也体现了 SVG 的强大功能。
首先,让我们从一些简化的 SVG 标记开始
<svg class="icon">
<path .../>
</svg>
在 CSS 中定位 .icon
类,并在悬停状态下设置 SVG 的 fill
属性以交换颜色。
.icon:hover {
fill: #DA4567;
}
这是迄今为止将彩色悬停状态应用于 SVG 的最简单方法。 三行代码!
SVG 也可以使用 <img>
标签或作为背景图像进行引用。 这允许图像被缓存,我们可以避免在 HTML 中填充大量的 SVG 代码。 但缺点也很明显:我们不再能够使用 CSS 操纵这些属性。 每当我遇到非内联图标时,我的第一个想法就是将它们内联,但有时这不是一种选择。
我最近在一个项目中工作,在这个项目中,社交图标是模式库中的一个组件,每个人对这个组件都很满意。 在这种情况下,图标是通过 <img>
元素引用的。 我的任务是在不调整标记的情况下,应用彩色 :focus
和 :hover
样式。
那么,如何为非内联 SVG 图标添加彩色悬停效果呢?
CSS 过滤器
CSS 过滤器 允许我们在浏览器中直接应用大量酷炫的、类似 Photoshop 的效果。 过滤器在浏览器呈现布局和初始绘制之后应用于元素,这意味着它们可以优雅地回退。 它们应用于整个元素,包括子元素。 可以将过滤器想象成覆盖在应用了过滤器的元素顶部的透镜。
这些是我们可用的 CSS 过滤器
brightness(<number-percentage>);
contrast(<number-percentage>);
grayscale(<number-percentage>);
invert(<number-percentage>);
opacity(<number-percentage>);
saturate(<number-percentage>);
sepia(<number-percentage>);
hue-rotate(<angle>);
blur(<length>);
drop-shadow(<length><color>);
所有过滤器都接受一个值,可以更改该值以调整效果。 在大多数情况下,该值可以用小数或百分比单位表示(例如,brightness(0.5)
或 brightness(50%)
)。
直接从头开始,没有 CSS 过滤器允许我们添加我们自己的特定颜色。
我们有 hue-rotate()
,但它只调整现有颜色;它不会添加颜色,这不好,因为我们从单色图标开始。
CSS 过滤器的改变之处在于,我们不必单独使用它们。 可以通过使用空格分隔过滤器函数将多个过滤器应用于元素,例如
.icon:hover {
filter: grayscale(100%) sepia(100%);
}
如果某个过滤器函数不存在,或具有不正确的值,则整个列表将被忽略,并且不会将任何过滤器应用于元素。
将多个过滤器函数应用于元素时,它们的顺序很重要,并且会影响最终输出。 每个过滤器函数都将应用于先前操作的结果。

因此,为了给图标着色,我们必须找到正确的组合。
为了使用 hue-rotate()
,我们需要从彩色图标开始。 sepia()
过滤器是唯一允许我们添加颜色的过滤器函数,它会使过滤后的元素呈现出黄色-棕色-y 色调,就像一张旧照片一样。
输出颜色取决于起始色调值

为了使用 sepia()
添加足够的颜色,我们首先需要使用 invert()
将图标转换为中等灰色
.icon:hover {
filter: invert(0.5)
}

然后我们可以使用 sepia()
添加黄色/棕色色调
.icon:hover {
filter: invert(0.5) sepia(1);
}

…然后使用 hue-rotate()
更改色调
.icon:hover {
filter: invert(0.5) sepia(1) hue-rotate(200deg);
}

一旦我们获得了想要的粗略颜色,就可以使用 saturation()
和 brightness()
微调它
.icon:hover {
filter:
invert(0.5)
sepia(1)
hue-rotate(200deg)
saturate(4)
brightness(1);
}

我为这个过程创建了一个小工具,以便让您的生活更轻松,因为这个过程的估计非常混乱。
查看 CodePen 上的 CSS 过滤器示例,由 Cassie Evans (@cassie-codes) 创建。
在 CodePen 上。
即使使用该工具,它仍然有点麻烦,不受 Internet Explorer 支持,最重要的是,您无法指定精确的颜色。
这些浏览器支持数据来自 Caniuse,它提供了更多详细信息。 数字表示浏览器从该版本开始支持该功能。
桌面
Chrome | Firefox | IE | Edge | Safari |
---|---|---|---|---|
18* | 35 | 否 | 79 | 6* |
移动设备/平板电脑
Android Chrome | Android Firefox | Android | iOS Safari |
---|---|---|---|
127 | 127 | 4.4* | 6.0-6.1* |
那么,如果我们需要一个特定的十六进制代码,该怎么办?
SVG 过滤器
如果我们需要比 CSS 过滤器所能提供的更精确的控制(以及更好的浏览器支持),那么就该转向 SVG 了。
过滤器最初来自 SVG。 事实上,在幕后,CSS 过滤器只是对具有特定值集的 SVG 过滤器的快捷方式。
与 CSS 不同,过滤器不是预先为我们定义的,因此我们必须创建它。 我们该怎么做呢?
这是定义过滤器的语法
<svg xmlns="<http://www.w3.org/2000/svg>" version="1.1">
<defs>
<filter id="id-of-your-filter">
...
...
</filter>
...
</defs>
</svg>
过滤器由 <filter>
元素定义,该元素位于 SVG 的 <defs>
部分。
SVG 过滤器可以应用于同一 SVG 文档中的 SVG 内容。 或者,可以引用过滤器并将其应用于其他地方的 HTML 内容。
要将 SVG 过滤器应用于 HTML 内容,我们像使用 CSS 过滤器一样引用它:使用 url()
过滤器函数。 URL 指向 SVG 过滤器的 ID。
.icon:hover {
filter: url('#id-of-your-filter');
}
SVG 过滤器可以放置在文档中的内联位置,或者过滤器函数可以引用外部 SVG。 我更喜欢后者,因为它允许我将 SVG 过滤器整理到资产文件夹中。
.icon:hover {
filter: url('assets/your-SVG.svg#id-of-your-filter');
}
回到 <filter>
元素本身。
<filter id="id-of-your-filter">
...
...
</filter>
目前,此过滤器为空,因为它没有定义过滤器基元,因此不会执行任何操作。 过滤器基元是创建过滤器效果的元素。 我们有许多可用的过滤器基元,包括
[<feBlend>]
[<feColorMatrix>]
[<feComponentTransfer>]
[<feComposite>]
[<feConvolveMatrix>]
[<feDiffuseLighting>]
[<feDisplacementMap>]
[<feDropShadow>]
[<feFlood>]
[<feGaussianBlur>]
[<feImage>]
[<feMerge>]
[<feMorphology>]
[<feOffset>]
[<feSpecularLighting>]
[<feTile>]
[<feTurbulence>]
就像 CSS 过滤器一样,我们可以单独使用它们,或者在 <filter>
标签中包含多个过滤器基元以获得更有趣的效果。 如果使用多个过滤器基元,则每个操作都将建立在先前操作的基础之上。
出于我们的目的,我们只使用 feColorMatrix
,但是如果您想了解有关 SVG 过滤器的更多信息,可以查看 MDN 上的规范 或 Sara Soueidan 启动的此(正在进行中,截至本文撰写时)文章系列。
feColourMatrix
允许我们按通道更改颜色值,就像 Photoshop 中的通道混合一样。
这就是语法的形式
<svg xmlns="<http://www.w3.org/2000/svg>" version="1.1">
<defs>
<filter id="id-of-your-filter">
<feColorMatrix
color-interpolation-filters="sRGB"
type="matrix"
values="1 0 0 0 0
0 1 0 0 0
0 0 1 0 0
0 0 0 1 0 "/>
</filter>
...
</defs>
</svg>
color-interpolation-filters
属性指定我们的颜色空间。 过滤器效果的默认颜色空间是 linearRGB,而在 CSS 中,RGB 颜色是在 sRGB 颜色空间中指定的。 重要的是,我们将其值设置为 sRGB,以使我们的颜色匹配。
让我们仔细看看颜色矩阵值。

前四列代表颜色的红、绿和蓝通道以及 alpha(不透明度)值。 行包含这些通道中的红、绿、蓝和 alpha 值。
M 列是一个乘数 - 我们不需要更改这里任何值。 每个颜色通道的值用 0 到 1 之间的浮点数表示。
我们可以将这些值写成类似这样的 CSS RGBA 颜色声明

每个颜色通道(红、绿和蓝)的值存储为 0 到 255 之间的整数。 在计算机中,这是单个 8 位字节可以提供的范围。
通过将这些颜色通道值除以 255,这些值可以用浮点数表示,我们可以在 feColorMatrix
中使用这些值。
而且,通过这样做,我们可以为具有 RGB 值的任何颜色创建颜色过滤器!
例如,像青绿色一样

查看 CodePen 上的
SVG 过滤器 - 青绿色悬停,由 Cassie Evans (@cassie-codes) 创建。
在 CodePen 上。
此 SVG 过滤器只会将颜色赋予具有白色填充的图标,因此,如果我们有一个具有黑色填充的图标,我们可以使用 invert()
将其转换为白色,然后再应用 SVG 过滤器。
.icon:hover {
filter: invert(100%) url('assets/your-SVG.svg#id-of-your-filter');
}
如果我们只有十六进制代码,数学运算会更复杂一些,尽管有很多十六进制到 RGBA 转换器。 为了帮助您,我创建了一个十六进制到 feColorMatrix 转换器。
查看 CodePen
HEX 到 feColorMatrix 转换器 由 Cassie Evans 创建 (@cassie-codes)
在 CodePen 上。
玩得开心,祝你过滤愉快!
很棒的文章!很高兴看到你使用过滤器。由于浏览器支持问题,我有一段时间没有使用它们,但现在对于我关心的浏览器来说,一切看起来都很正常,是时候再次使用它们了。我想知道我们是否可以使用过滤器进行渐变填充
酷!
你不需要将填充设置为白色才能使用 feColorMatrix。第五列允许你直接在黑色填充上指定颜色。重要的是在过滤器元素中添加 color-interpolation-filters=“sRGB”,以便颜色预期值匹配。
具体来说,虽然文本将第五列描述为“乘数”,但实际上它是一个添加到结果中的固定偏移量(由于矩阵乘法的运作方式)。因此,要创建将任何输入颜色转换为特定 RGB 颜色的过滤器,可以使用如下矩阵
0 0 0 0 (R/255)
0 0 0 0 (G/255)
0 0 0 0 (B/255)
0 0 0 1 0
用你的红色值除以 255 的结果替换 (R/255),以此类推。
感谢您的反馈!我会更新文章。
很棒的文章。SVG 过滤器的缺点似乎是浏览器支持较少(同样,不支持 IE,也不支持 Edge,可能还有一些其他浏览器)。
https://caniuse.cn/#feat=svg-filters 实际上,SVG 过滤器有更好的支持!
酷。感谢分享
关于 Michael 的说法,你可以只使用矩阵的“M”列来表示你的 RGB 目标输出值,并使输入到输出的 alpha 值为 1:1,这样显示的颜色就不会依赖于输入颜色,而只有输入 alpha 值才会影响它。
如果你只使用单一颜色,可以使用遮罩在悬停时更改填充:https://codepen.io/noahblon/post/coloring-svgs-in-css-background-images#css-masks-1