如何在 SVG 中制作插入阴影
Posted
技术标签:
【中文标题】如何在 SVG 中制作插入阴影【英文标题】:How to make an inset drop shadow in SVG 【发布时间】:2014-01-13 17:45:05 【问题描述】:我需要制作一个带有嵌入阴影的盒子,就像 CSS3 有嵌入的盒子阴影一样。到目前为止,我发现的是一个带有 feGaussianBlur 的过滤器,但问题是它还在框外添加了一个阴影,这是我不想要的。这是我目前得到的代码:
<svg>
<defs>
<filter id="drop-shadow">
<feGaussianBlur in="SourceAlpha" result="blur" stdDeviation="5" />
<feGaussianBlur in="SourceAlpha" result="blur2" stdDeviation="10" />
<feGaussianBlur in="SourceAlpha" result="blur3" stdDeviation="15" />
<feMerge>
<feMergeNode in="blur" mode="normal"/>
<feMergeNode in="blur2" mode="normal"/>
<feMergeNode in="blur3" mode="normal"/>
<feMergeNode in="SourceGraphic" mode="normal"/>
</feMerge>
</filter>
</defs>
<rect x="10" y="10"
stroke="black" stroke- fill="transparent" filter="url(#drop-shadow)"/>
</svg>
我已经创建了一个demo,它还将此代码与所需的 CSS 生成的结果进行比较。过滤器不仅适用于矩形,还适用于梯形和更复杂的多边形。
我已经尝试过使用radialGradient,但是因为这会使渐变变成圆形,所以也不好。
【问题讨论】:
对于 SVG 代码不太了解的用户,superuser.com/questions/128814/… 有一个相关问题(“Inkscape:如何创建内边框?”)。 【参考方案1】:如果你有一个实心填充,你可以添加
<feComposite operator="in" in2="SourceGraphic"/>
到过滤器的末尾,它会将模糊剪辑到 SourceGraphic 的形状。由于您的形状是透明的,因此您需要做更多的工作。我的建议是在原始图形上使用半透明填充,以便获得正确的合成选择,并使用 feFuncA 将填充归零以进行最终操作。事实证明这非常复杂。但这里有一个适用于任何实心笔触形状的解决方案
<filter id="inset-shadow" >
<!-- dial up the opacity on the shape fill to "1" to select the full shape-->
<feComponentTransfer in="SourceAlpha" result="inset-selection">
<feFuncA type="discrete" tableValues="0 1 1 1 1 1"/>
</feComponentTransfer>
<!-- dial down the opacity on the shape fill to "0" to get rid of it -->
<feComponentTransfer in="SourceGraphic" result="original-no-fill">
<feFuncA type="discrete" tableValues="0 0 1"/>
</feComponentTransfer>
<!-- since you can't use the built in SourceAlpha generate your own -->
<feColorMatrix type="matrix" in="original-no-fill" result="new-source-alpha" values="0 0 0 0 0
0 0 0 0 0
0 0 0 0 0
0 0 0 1 0"
/>
<feGaussianBlur in="new-source-alpha" result="blur" stdDeviation="5" />
<feGaussianBlur in="new-source-alpha" result="blur2" stdDeviation="10" />
<feGaussianBlur in="new-source-alpha" result="blur3" stdDeviation="15" />
<feMerge result="blur">
<feMergeNode in="blur" mode="normal"/>
<feMergeNode in="blur2" mode="normal"/>
<feMergeNode in="blur3" mode="normal"/>
</feMerge>
<!-- select the portion of the blur that overlaps with your shape -->
<feComposite operator="in" in="inset-selection" in2="blur" result="inset-blur"/>
<!-- composite the blur on top of the original with the fill removed -->
<feComposite operator="over" in="original-no-fill" in2="inset-blur"/>
</filter>
这是我的小提琴叉:http://jsfiddle.net/kkPM4/
【讨论】:
这很好用,非常感谢!不过,我有一个关于你的小提琴的问题,当我尝试更改<rect>
的填充不透明度时,当填充不透明度低于 17% 或高于 66% 时,插入的阴影突然消失了(有趣的是能够让不支持过滤器的浏览器的不透明度尽可能低,当然),而在该范围内更改时完全不更改。有什么特别的原因吗?
这就是我在里面的两个 feFuncA 的效果。他们分别将 alpha 范围分成七个和三个范围,并将这些范围内的任何不透明度分别设置为 0、1、1、1、1、1、1 和 0、0、1。如果您想将填充不透明度设置得非常低(例如 0.03),您必须适当调整这些 tableValues(例如 0 后跟 49 个 1)。 IE 仅支持该数组中的 64 个值,因此不透明度不能低于 1/64。【参考方案2】:
主要基于experiment that I found,这是我想出的:
<defs><filter id="inset-shadow">
<feOffset dx="10" dy="10"/> <!-- Shadow Offset -->
<feGaussianBlur stdDeviation="10" result="offset-blur"/> <!-- Shadow Blur -->
<feComposite operator="out" in="SourceGraphic" in2="offset-blur" result="inverse"/> <!-- Invert the drop shadow to create an inner shadow -->
<feFlood flood-color="black" flood-opacity="1" result="color"/> <!-- Color & Opacity -->
<feComposite operator="in" in="color" in2="inverse" result="shadow"/> <!-- Clip color inside shadow -->
<feComponentTransfer in="shadow" result="shadow"> <!-- Shadow Opacity -->
<feFuncA type="linear" slope=".75"/>
</feComponentTransfer>
<!--<feComposite operator="over" in="shadow" in2="SourceGraphic"/>--> <!-- Put shadow over original object -->
</filter></defs>
<rect fill="yellow" filter="url(#inset-shadow)"/>
如果您希望看到填充,请取消注释最后一个 <feComposite>
。不幸的是,fill="transparent"
不会为过滤器提供 alpha 以供使用,并且不会产生阴影。
【讨论】:
以上是关于如何在 SVG 中制作插入阴影的主要内容,如果未能解决你的问题,请参考以下文章
如何通过 CSS 将阴影过滤器应用于 SVG 特定元素/路径