使用 CSS 和 SVG 滤镜为 SVG 图标添加阴影

Avatar of Joel Olawanle
Joel Olawanle

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

为什么要将阴影应用于 SVG?

  1. 阴影是一种常见的​​设计功能,可以帮助元素(如图标)脱颖而出。它们可以是持久的,也可以应用于不同的状态(例如 :hover:focus:active),以向用户指示交互。
  2. 阴影在现实生活中发生,因此它们可以用于屏幕上,为您的元素注入活力并 为设计增添一丝真实感

由于我们要列出,因此主要有两种方法可以将阴影应用于 SVG

  1. 使用 CSS filter() 属性
  2. 使用 SVG <filter>

是的,两者都涉及滤镜!而且,是的,CSS 和 SVG 都有自己的滤镜类型。但它们之间也有一些交叉。例如,CSS filter 可以引用 SVG <filter>;也就是说,如果我们使用的是内联 SVG,而不是,比如说,在 CSS 中用作背景图像的 SVG。

您不能使用什么:CSS box-shadow 属性。这通常用于阴影,但它遵循元素的矩形外边缘,而不是我们想要的 SVG 元素的边缘。以下是 Michelle Barker 对 清晰解释

Two flat kitten faces in bright pink showing ears eyes and whiskers. The first kitten has a drop shadow around its box and the second kitten has a drop shadow around its path edges.

但是,如果您使用的是 SVG 图标字体,那么始终可以使用 text-shadow。那确实有效。但让我们专注于前两者,因为它们符合大多数用例。

使用 CSS 滤镜的阴影

通过 CSS 滤镜直接将阴影应用于 SVG 的技巧是 drop-shadow() 函数

svg {
  filter: drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));
}

这将应用一个阴影,该阴影从水平方向 3px、向下 5px 开始,模糊度为 2px,并且为 40% 的黑色。以下是一些示例

此浏览器支持数据来自 Caniuse,其中包含更多详细信息。数字表示浏览器在该版本及更高版本支持该功能。

台式机

ChromeFirefoxIEEdgeSafari
18*35796*

移动设备/平板电脑

Android ChromeAndroid FirefoxAndroidiOS Safari
1271274.4*6.0-6.1*

在 CSS 滤镜中调用 SVG 滤镜

假设我们在 HTML 中有一个 SVG 滤镜

<svg height="0" width="0">
  
  <filter id='shadow' color-interpolation-filters="sRGB">
    <feDropShadow dx="2" dy="2" stdDeviation="3" flood-opacity="0.5"/>
  </filter>
  
</svg>

我们可以使用 CSS 滤镜通过 ID 调用该 SVG 滤镜,而不是我们之前看到的数值

svg {
  filter: url(#shadow);
}

现在,该滤镜是从 HTML 中获取的,并在 CSS 中引用,这会应用它。

使用 SVG 滤镜原语

您可能想知道我们如何让 SVG <filter> 工作。要使用 SVG 滤镜创建投影,我们使用滤镜原语。SVG 中的滤镜原语是一种元素,它以某种图像或图形作为输入,然后在被调用时输出该图像或图形。它们有点像图形编辑应用程序中的滤镜,但在代码中,只能在 SVG <filter> 元素内使用。

SVG 中有 许多不同的滤镜原语。我们要使用的是 <feDropShadow>。我让您通过查看名称来猜测它做了什么。

因此,类似于我们使用 CSS 滤镜执行以下操作的方式

svg {
  filter: drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));
}

…我们可以使用 <feDropShadow> SVG 滤镜原语实现相同的效果。有三个关键属性值得关注,因为它们有助于定义投影的外观

  • dx — 这会沿着 x 轴移动阴影的位置。
  • dy — 这会沿着 y 轴移动阴影的位置。
  • stdDeviation — 这定义了投影模糊操作的标准差。我们可以使用其他属性,例如用于设置投影颜色的 flood-color,以及用于设置投影不透明度的 flood-opacity

该示例包含三个 <filter> 元素,每个元素都有自己的 <feDropShadow> 滤镜原语。

使用 SVG 滤镜

SVG 滤镜非常强大。我们刚刚查看了 <feDropShadow>,它非常有用,当然,但它们还能做更多事情(包括 Photoshop 风格的效果),而我们仅用于阴影的子集非常广泛。让我们看看一些,例如彩色阴影和内嵌阴影。

以 Twitter 徽标的 SVG 标记为例

<svg class="svg-icon" viewBox="0 0 20 20">
  <path fill="#4691f6" d="M18.258,3.266c-0.693,0.405-1.46,0.698-2.277,0.857c-0.653-0.686-1.586-1.115-2.618-1.115c-1.98,0-3.586,1.581-3.586,3.53c0,0.276,0.031,0.545,0.092,0.805C6.888,7.195,4.245,5.79,2.476,3.654C2.167,4.176,1.99,4.781,1.99,5.429c0,1.224,0.633,2.305,1.596,2.938C2.999,8.349,2.445,8.19,1.961,7.925C1.96,7.94,1.96,7.954,1.96,7.97c0,1.71,1.237,3.138,2.877,3.462c-0.301,0.08-0.617,0.123-0.945,0.123c-0.23,0-0.456-0.021-0.674-0.062c0.456,1.402,1.781,2.422,3.35,2.451c-1.228,0.947-2.773,1.512-4.454,1.512c-0.291,0-0.575-0.016-0.855-0.049c1.588,1,3.473,1.586,5.498,1.586c6.598,0,10.205-5.379,10.205-10.045c0-0.153-0.003-0.305-0.01-0.456c0.7-0.499,1.308-1.12,1.789-1.827c-0.644,0.28-1.334,0.469-2.06,0.555C17.422,4.782,17.99,4.091,18.258,3.266" ></path>
</svg>

我们需要一个 <filter> 元素来实现这些效果。这需要在 HTML 中的 <svg> 元素内。<filter> 元素永远不会直接在浏览器中呈现——它仅用作可以通过 SVG 中的 filter 属性或 CSS 中的 url() 函数引用的东西。

以下语法显示了 SVG 滤镜及其应用于源图像的方式

<svg width="300" height="300" viewBox="0 0 300 300">

  <filter id="myfilters">
    <!-- All filter effects/primitives go in here -->
  </filter>

  <g filter="url(#myfilters)">
    <!-- Filter applies to everything in this group -->
    <path fill="..." d="..." ></path>
  </g>

</svg>

filter 元素旨在将滤镜原语作为子元素包含在内。它是一系列滤镜操作的容器,这些操作组合在一起以创建滤镜效果。

这些滤镜原语对一个或多个输入执行单个基本图形操作(例如模糊、移动、填充、组合或扭曲)。它们就像积木,每个 SVG 滤镜可以与其他滤镜一起使用以创建效果。<feGaussianBlur> 是一个常用的滤镜原语,用于添加模糊效果。

假设我们使用 <feGaussianBlur> 定义了以下 SVG 滤镜

<svg version="1.1" width="0" height="0">
  <filter id="gaussian-blur">
    <feGaussianBlur stdDeviation="1 0" />
  </filter>
</svg>

应用于元素后,此滤镜会在元素上创建 高斯模糊,在 x 轴上以 1px 半径模糊元素,但在 y 轴上不模糊。以下是结果,有无效果

可以在单个滤镜中使用多个原语。这将创建有趣的效果,但是,您需要让不同的原语互相了解。Bence Szabó 使用这种方法创建了一组 非常酷的图案

组合多个滤镜原语时,第一个原语使用原始图形(SourceGraphic)作为其图形输入。任何后续原语都使用其之前的滤镜效果的结果作为其输入。依此类推。但我们可以通过在原语元素上使用 inin2result 属性获得一些灵活性。 Steven Bradley 有一篇关于滤镜原语的优秀文章,可以追溯到 2016 年,但现在仍然适用。

我们今天可以使用 17 个原语

  • <feGaussianBlur>
  • <feDropShadow>
  • <feMorphology>
  • <feDisplacementMap>
  • <feBlend>
  • <feColorMatrix>
  • <feConvolveMatrix>
  • <feComponentTransfer>
  • <feSpecularLighting>
  • <feDiffuseLighting>
  • <feFlood>
  • <feTurbulence>
  • <feImage>
  • <feTile>
  • <feOffset>
  • <feComposite>
  • <feMerge>

请注意它们都有 fe 前缀。它代表滤镜效果。理解 SVG 滤镜具有挑战性。像内嵌阴影这样的效果需要冗长的语法,如果没有对数学和颜色理论的透彻理解,很难理解。(Rob O’Leary 的 “深入阴影” 是一个不错的起点。)

我们不打算深入探讨这些内容,而是要使用一些预制滤镜。幸运的是,有很多现成的 SVG 滤镜。

内嵌阴影

要在 Twitter 徽标上使用滤镜效果,我们需要在我们的“SVG 源文档”中声明它,并在我们的 <filter> 标记中使用唯一的 ID 来引用它。

<filter id='inset-shadow'>
  <!-- Shadow offset -->
  <feOffset
    dx='0'
    dy='0'
  />

  <!-- Shadow blur -->
  <feGaussianBlur
    stdDeviation='1'
    result='offset-blur'
  />

  <!-- Invert drop shadow to make an inset shadow -->
  <feComposite
    operator='out'
    in='SourceGraphic'
    in2='offset-blur'
    result='inverse'
  />
  
  <!-- Cut color inside shadow -->
  <feFlood
    flood-color='black'
    flood-opacity='.95'
    result='color'
  />
  <feComposite
    operator='in'
    in='color'
    in2='inverse'
    result='shadow'
  />

  <!-- Placing shadow over element -->
  <feComposite
    operator='over'
    in='shadow'
    in2='SourceGraphic'
  />
</filter>

其中有四个不同的原语,每个原语执行不同的功能。但是,总体上,它们实现了内嵌阴影。

现在我们已经创建了这个内嵌阴影滤镜,我们可以将其应用于我们的 SVG。我们已经了解了如何通过 CSS 应用它。类似于

.filtered {
  filter: url(#myfilters);
}

/* Or apply only in certain states, like: */
svg:hover, svg:focus {
  filter: url(#myfilters);
} 

我们也可以使用 filter 属性直接在 SVG 语法中应用 SVG <filter>。就像

<svg>

  <!-- Apply a single filter -->
  <path d="..." filter="url(#myfilters)" />

  <!-- Or apply to a whole group of elements -->
  <g filter="url(#myfilters)">
    <path d="..." />
    <path d="..." />
  </g>
</svg>

更多示例

以下是一些来自 Oleg Solomka 的更多阴影示例

请注意,这里的基本阴影可能比实际需要的复杂一些。例如,可以使用``创建彩色阴影,如

<feDropShadow dx="-0.8" dy="-0.8" stdDeviation="0"
  flood-color="pink" flood-opacity="0.5"/>

但是,这种浮雕效果作为过滤器来说非常棒!

另外请注意,您可能会看到类似于以下的 SVG 语法中的 SVG 过滤器:

<svg height="0" width="0" style="position: absolute; margin-left: -100%;">
  <defs>
    <filter id="my-filters">
      <!-- ... -->
    </filter>

    <symbol id="my-icon">
      <!-- ... -->
    </symbol>
  </defs>
</svg>

第一行表示:这个 SVG 不应该渲染——它只是我们打算稍后使用的内容。`` 标签表示类似的意思:我们只是定义了这些内容,以便稍后使用。这样,我们就不必重复编写这些内容。我们可以通过 ID 引用过滤器和符号,例如

<svg>
  <use xlink:href="#my-icon" />
</svg>

SVG 过滤器具有广泛的支持(即使在 Internet Explorer 和 Edge 中!),而且性能非常快。

这些浏览器支持数据来自 Caniuse,其中包含更多详细信息。数字表示浏览器从该版本开始支持该功能。

台式机

ChromeFirefoxIEEdgeSafari
8310126

移动设备/平板电脑

Android ChromeAndroid FirefoxAndroidiOS Safari
1271274.46.0-6.1

总结

最终比较

  • CSS 过滤器更易于使用,但功能有限。例如,我认为无法使用 `drop-shadow()` 函数添加内阴影。
  • SVG 过滤器更加健壮,但也更加复杂,并且需要在 HTML 中包含 ``。
  • 它们都具有良好的浏览器支持,并在所有现代浏览器中表现良好,尽管 SVG 过滤器(令人惊讶的是)具有最广泛的浏览器支持。

在本文中,我们已经了解了为什么以及如何使用示例对 SVG 图标应用阴影。您是否使用过其他方法来实现这一点?您是否尝试过进行一些您发现无法实现的阴影效果?请分享您的经验!