在悬停时更改 SVG 填充的多种方法(以及何时使用它们)

Avatar of Cassie Evans
Cassie Evans on

DigitalOcean 为您的旅程的每个阶段提供云产品。 立即开始使用 $200 免费积分!

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,它提供了更多详细信息。 数字表示浏览器从该版本开始支持该功能。

桌面

ChromeFirefoxIEEdgeSafari
18*35796*

移动设备/平板电脑

Android ChromeAndroid FirefoxAndroidiOS Safari
1271274.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 颜色声明

rgba(255, 255, 255, 1)

每个颜色通道(红、绿和蓝)的值存储为 0 到 255 之间的整数。 在计算机中,这是单个 8 位字节可以提供的范围。

通过将这些颜色通道值除以 255,这些值可以用浮点数表示,我们可以在 feColorMatrix 中使用这些值。

而且,通过这样做,我们可以为具有 RGB 值的任何颜色创建颜色过滤器!

例如,像青绿色一样

rgba(0, 128, 128, 1). 128%255=0.50

查看 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 上。

玩得开心,祝你过滤愉快!