为什么要将阴影应用于 SVG?
- 阴影是一种常见的设计功能,可以帮助元素(如图标)脱颖而出。它们可以是持久的,也可以应用于不同的状态(例如
:hover
、:focus
或:active
),以向用户指示交互。 - 阴影在现实生活中发生,因此它们可以用于屏幕上,为您的元素注入活力并 为设计增添一丝真实感。
由于我们要列出,因此主要有两种方法可以将阴影应用于 SVG
- 使用 CSS
filter()
属性 - 使用 SVG
<filter>
是的,两者都涉及滤镜!而且,是的,CSS 和 SVG 都有自己的滤镜类型。但它们之间也有一些交叉。例如,CSS filter
可以引用 SVG <filter>
;也就是说,如果我们使用的是内联 SVG,而不是,比如说,在 CSS 中用作背景图像的 SVG。
您不能使用什么:CSS box-shadow
属性。这通常用于阴影,但它遵循元素的矩形外边缘,而不是我们想要的 SVG 元素的边缘。以下是 Michelle Barker 对 清晰解释

但是,如果您使用的是 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,其中包含更多详细信息。数字表示浏览器在该版本及更高版本支持该功能。
台式机
Chrome | Firefox | IE | Edge | Safari |
---|---|---|---|---|
18* | 35 | 否 | 79 | 6* |
移动设备/平板电脑
Android Chrome | Android Firefox | Android | iOS Safari |
---|---|---|---|
127 | 127 | 4.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
)作为其图形输入。任何后续原语都使用其之前的滤镜效果的结果作为其输入。依此类推。但我们可以通过在原语元素上使用 in
、in2
和 result
属性获得一些灵活性。 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 不应该渲染——它只是我们打算稍后使用的内容。`
<svg>
<use xlink:href="#my-icon" />
</svg>
SVG 过滤器具有广泛的支持(即使在 Internet Explorer 和 Edge 中!),而且性能非常快。
这些浏览器支持数据来自 Caniuse,其中包含更多详细信息。数字表示浏览器从该版本开始支持该功能。
台式机
Chrome | Firefox | IE | Edge | Safari |
---|---|---|---|---|
8 | 3 | 10 | 12 | 6 |
移动设备/平板电脑
Android Chrome | Android Firefox | Android | iOS Safari |
---|---|---|---|
127 | 127 | 4.4 | 6.0-6.1 |
总结
最终比较
- CSS 过滤器更易于使用,但功能有限。例如,我认为无法使用 `drop-shadow()` 函数添加内阴影。
- SVG 过滤器更加健壮,但也更加复杂,并且需要在 HTML 中包含 `
`。 - 它们都具有良好的浏览器支持,并在所有现代浏览器中表现良好,尽管 SVG 过滤器(令人惊讶的是)具有最广泛的浏览器支持。
在本文中,我们已经了解了为什么以及如何使用示例对 SVG 图标应用阴影。您是否使用过其他方法来实现这一点?您是否尝试过进行一些您发现无法实现的阴影效果?请分享您的经验!
为什么您使用 rgb(0 0 0 / 0.4) 而不是 rbgba(0, 0, 0, .4)?
感谢您的回复。
这是颜色的新语法:https://www.w3.org/TR/css-color-4/#rgb-functions
让我们谈谈性能。在现实生活中,通常不建议使用这些方法,而且过度使用它们非常危险。例如,当我用我的(高端)手机滚动浏览这篇文章时,每次出现演示时,我的屏幕就开始卡顿,直到图形显示出来。
如果您需要在多个图标上多次使用这些方法,那么这是不可行的。
我希望这里能看到一些数据或其他内容。我并不怀疑存在一些问题,但比如,在一个页面上对几个图标使用 `filter`,这会导致高端手机运行缓慢?太奇怪了。有没有人对此有更深入的了解?
感谢您,但是与更差的浏览器支持相比(rgb 与 rgba),有什么好处吗?
我注意到在心形(❤️)示例(每次我滚动经过它时)处有一个轻微的卡顿,我使用的是搭载骁龙 636 的中端手机,运行的是 Android 10,通过 Android Webview 查看页面。
不过,对于页面上的其他内容,我没有注意到任何问题,再说这个页面只是为了演示使用阴影的不同方法,而它的 CSS 也是如此,所以我怀疑为此页面记录性能分析会给任何人带来任何可以推断的信息,而且我认为任何页面都不可能包含如此大量的带有阴影和效果的 SVG 图标。
尽管如此,我意识到在旧硬件上进行一些分析可能会证明并非如此,但除非这些数据有助于提高浏览器的渲染性能,而这并不是前端开发人员的责任,仅仅是为了找到最佳方法,让大多数浏览器不会出现问题,而不是直接修改浏览器引擎本身以提高渲染性能。
太棒了!感谢您分享这些内容。
是否支持过渡和动画?
是的!......我认为我用了一些过渡效果来实现悬停效果,效果很好!
是否可以对一组 SVG 添加阴影,而不将它们合并为一个?
这看起来正是我为我的拟物化项目想要的效果。我正在制作一些动画,让它看起来像“升起”,但我注意到,特别是在性能不如我的电脑上,CSS 过滤器看起来不太流畅。
我并不关心 Internet Explorer。现在是 2022 年了,人们。