How to make an inset drop shadow in SVG - css

I need to make a box with an inset drop shadow, in the same way that CSS3 has inset box-shadows. What I've found so far is a filter with feGaussianBlur, but the problem with that is that it also adds a drop shadow outside the box, which I don't want. Here's the code I've got so far:
<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" width="100" height="100"
stroke="black" stroke-width="4" fill="transparent" filter="url(#drop-shadow)"/>
</svg>
I've made a demo that also compares this code with the desired CSS-made result. The filter should not just work on rectangles, but also on trapezoids and more complicated polygons.
I've already tried using radialGradient, but since that makes the gradient circular, that's not good either.

If you had a solid fill, you could just add
<feComposite operator="in" in2="SourceGraphic"/>
to the end of your filter and it would clip the blur to the shape of the SourceGraphic. Since your shape is transparent, you'll have to do a little more work. What I'd suggest is using a semi-transparent fill on the original graphic in order to get the right selection for compositing and using an feFuncA to zero out the fill for the final operation. This turns out to be surprisingly complicated. But here is a solution that will work for any solid-stroke shape
<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>
here is my fork of your fiddle: http://jsfiddle.net/kkPM4/

largely based on an experiment that I found, here's what I've come up with:
<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 width="100" height="100" fill="yellow" filter="url(#inset-shadow)"/>
If you want the fill to be seen, uncomment the last <feComposite>. Unfortunately, fill="transparent" will not give the filter an alpha to work with and will produce no shadow.

Related

Ripple issue when I give box shadow inset value

I'm trying to do the Mapbox design. There is a fog on the globe in Mapbox. I use "box shadow inset" for the same effect. But because I give high value, there are various fluctuations.
My Try;
enter image description here
The effect I use to give the fog;
<svg>
<defs>
<filter id="shadow">
<feFlood flood-color="gray" />
<feComposite operator="out" in2="SourceGraphic" />
<feMorphology operator="dilate" radius="20" />
<feGaussianBlur stdDeviation="50" />
<feComposite operator="atop" in2="SourceGraphic" />
</filter>
</defs>
</svg>
What I Want To Do (Mapbox, hollow of fog cloud);
enter image description here

SVG Filter inset shadow not working in Safari

Hello I'm trying to apply this svg inset shadow filter to my div. Is working fine in Chrome but not in Safari.
Here's the svg filter:
<filter id="inset" primitiveUnits="objectBoundingBox" x="0%" y="0%">
<feOffset dx="0.00" dy="0.00"></feOffset>
<feGaussianBlur stdDeviation="0.1" result="offset-blur"></feGaussianBlur>
<feComposite operator="out" in="SourceGraphic" in2="offset-blur" result="inverse"></feComposite>
<feFlood flood-color="black" flood-opacity="0.8" result="color"></feFlood>
<feComposite operator="in" in="color" in2="inverse" result="shadow"></feComposite>
<feComposite operator="over" in="shadow" in2="SourceGraphic" result="inset-shadow"></feComposite>
<feGaussianBlur in="SourceAlpha" stdDeviation="0.0012"></feGaussianBlur>
<feOffset dx="0.013" dy="0.013" result="offsetblur"></feOffset>
<feFlood flood-color="black"></feFlood>
<feComposite in2="offsetblur" operator="in"></feComposite>
<feMerge>
<feMergeNode in="inset-shadow"></feMergeNode>
</feMerge>
</filter>
Where's the problem?
Note: The child where I' trying to applying this filter has a clip-path polygon property
Chrome result
Safari result
Safari does not support certain SVG filter primitives when they're used via a CSS filter. feComposite/out is one of them.

Why my two inset shadow in svg filters doesn't work?

I'm try make a two inset shadow in svg element, but it doesn't work. First shadow is overlaying on second.
This is a filter code:
<filter id="shadow-r-l" x="-50%" y="-50%" width="200%" height="200%">
<feOffset dx="50" dy="50" result="offset2"/>
<feGaussianBlur stdDeviation="8" in="offset2" result="offset-blur2"/>
<feComposite operator="out" in="SourceGraphic" in2="offset-blur2" result="inverse2" />
<feFlood flood-color="black" flood-opacity="0.5" result="color2" />
<feComposite operator="in" in="color2" in2="inverse2" result="shadow2" />
<feComposite operator="over" in="shadow2" in2="SourceGraphic" result="inset-shadow2" />
<feOffset dx="20" dy="20" result="offset"/>
<feGaussianBlur stdDeviation="8" in="offset" result="offset-blur"/>
<feComposite operator="out" in="SourceGraphic" in2="offset-blur" result="inverse" />
<feFlood flood-color="white" flood-opacity="0.5" result="color" />
<feComposite operator="in" in="color" in2="inverse" result="shadow" />
<feComposite operator="over" in="shadow" in2="SourceGraphic" result="inset-shadow" />
<feMerge>
<feMergeNode/>
<feMergeNode in="inset-shadow2" />
<feMergeNode in="inset-shadow" />
</feMerge>
</filter>
The code is creating "inset-shadow2" result which is the source image with the upper left edge darked. The code is then creating the "inset-shadow" result which is the source image with the upper left edge lighten. The code is then using feMerge to paint "inset-shadow" result over the "inset-shadow2" result. Since the "inset-shadow" and "inset-shadow2" results take up the same space, the "inset-shadow" result will completely over write the "inset-shadow2" result. I assume you intended to combine the light and dark effects over the source image. If so, you can replace the feMerge element with multiple feComposite elements.
The code is not specifying the in attribute on the feOffset elements. This could cause problems for the second feOffset element which I assume was intended to use SourceGraphic for input.
Making these changes yields the following code...
<filter id="shadow-r-l" x="-50%" y="-50%" width="200%" height="200%">
<feOffset dx="50" dy="50" in="SourceGraphic" result="offset2"/>
<feGaussianBlur stdDeviation="8" in="offset2" result="offset-blur2"/>
<feComposite operator="out" in="SourceGraphic" in2="offset-blur2" result="inverse2" />
<feFlood flood-color="black" flood-opacity="0.5" result="color2" />
<feComposite operator="in" in="color2" in2="inverse2" result="shadow2" />
<feOffset dx="20" dy="20" in="SourceGraphic" result="offset"/>
<feGaussianBlur stdDeviation="8" in="offset" result="offset-blur"/>
<feComposite operator="out" in="SourceGraphic" in2="offset-blur" result="inverse" />
<feFlood flood-color="white" flood-opacity="0.5" result="color" />
<feComposite operator="in" in="color" in2="inverse" result="shadow" />
<feComposite operator="over" in="shadow2" in2="SourceGraphic" result="inset-shadow2" />
<feComposite operator="over" in="shadow" in2="inset-shadow2" result="inset-shadow2-shadow" />
</filter>

How do I create a svg drop shadow?

I've been using a bunch of tutorials to try to get my svg to have a drop shadow, but nothing is working!
Here's my fiddle: https://jsfiddle.net/5nfdovg5/
This is what I did:
Created a gradient fill called "Gradient-1". This works:
When I add the dropshadow filter (called "dropshadow"), it only shows the shadow, and none of the circle that is supposed to be on top.. just a black blurry circle:
The desired effect I would like is here:
http://xn--dahlstrm-t4a.net/svg/filters/arrow-with-dropshadow-lighter.svg
Any ideas on what I'm doing wrong?
Here's the full markup:
<svg width="200" height="200">
<defs>
<linearGradient id="Gradient-1" x1="20%" y1="30%" x2="40%" y2="80%">
<stop offset="0%" stop-color="#B8D0DE"></stop>
<stop offset="100%" stop-color="#73A2BD"></stop>
</linearGradient>
<filter id="dropshadow" xmlns="http://www.w3.org/2000/svg" height="130%">
<feGaussianBlur in="SourceAlpha" stdDeviation="3">
<feOffset dx="2" dy="2" result="offsetblur">
<feComponentTransfer>
<feFuncA type="linear" slope="0.2"></feFuncA>
</feComponentTransfer>
<feMerge>
<feMergeNode>
<feMergeNode in="SourceGraphic"></feMergeNode>
</feMergeNode>
</feMerge>
</feOffset>
</feGaussianBlur>
</filter>
</defs>
<circle cx="125" cy="125" r="25" filter="url(#dropshadow)" fill="url(#Gradient-1)"></circle>
</svg>
You seem to have nested the feFilter elements which the example drop shadow code you link to in the question does not do. Unnesting makes things work as expected.
<svg width="200" height="200">
<defs>
<linearGradient id="Gradient-1" x1="20%" y1="30%" x2="40%" y2="80%">
<stop offset="0%" stop-color="#B8D0DE"></stop>
<stop offset="100%" stop-color="#73A2BD"></stop>
</linearGradient>
<filter id="dropshadow" xmlns="http://www.w3.org/2000/svg" height="130%" width="130%">
<feGaussianBlur in="SourceAlpha" stdDeviation="3"/>
<feOffset dx="2" dy="2" result="offsetblur"/>
<feComponentTransfer>
<feFuncA type="linear" slope="0.2"></feFuncA>
</feComponentTransfer>
<feMerge>
<feMergeNode/>
<feMergeNode in="SourceGraphic"></feMergeNode>
</feMerge>
</filter>
</defs>
<circle cx="125" cy="125" r="25" filter="url(#dropshadow)" fill="url(#Gradient-1)"></circle>
</svg>

SVG filter feGaussianBlur in percentage

Is it possible to set the feGaussianBlur in %?
I don't know why but this does not work.
<filter id="drop-shadow">
<feGaussianBlur in="SourceAlpha" stdDeviation="0.09"/>
<feOffset dx="52%" dy="2%" result="offsetblur"/>
<feFlood flood-color="#000000"/>
<feComposite in2="offsetblur" operator="in"/>
<feMerge>
<feMergeNode/>
<feMergeNode in="SourceGraphic"/>
</feMerge>
</filter>
Paul and Robert mean the following:
<filter id="drop-shadow" primitiveUnits="objectBoundingBox">
<feGaussianBlur in="SourceAlpha" stdDeviation="0.09"/>
<feOffset dx="0.52" dy="0.02" result="offsetblur"/>
<feFlood flood-color="#000000"/>
<feComposite in2="offsetblur" operator="in"/>
<feMerge>
<feMergeNode/>
<feMergeNode in="SourceGraphic"/>
</feMerge>
</filter>
That is a very big blur and a very big offset in % terms, so you're going to have to expand the filter region a lot.
<svg width="800" height="800">
<defs>
<filter id="blurme" primitiveUnits="objectBoundingBox" x="-50%" y="-50%" width="300%" height="200%">
<feGaussianBlur in="SourceAlpha" stdDeviation="0.09"/>
<feOffset dx="0.52" dy="0.02" result="offsetblur"/>
<feFlood flood-color="black"/>
<feComposite in2="offsetblur" operator="in"/>
<feMerge>
<feMergeNode/>
<feMergeNode in="SourceGraphic"/>
</feMerge>
</filter>
</defs>
<rect filter="url(#blurme)" x="100" y="100" width="300" height="300" fill="red"/>
</svg>
The SVG specification says that only numbers are allowed, although if you use objectBoundingBox the number is effectively a percentage of the object's size. There's an example of filterPrimitiveUnits in the SVG specification

Resources