SVG filter with variable? - html

I have an SVG glow filter implemented like so:
<filter id="outline">
<feMorphology in="SourceAlpha" operator="dilate" radius="2"></feMorphology>
<feGaussianBlur stdDeviation="1" result="dilated"></feGaussianBlur>
<feFlood style="flood-color: #RRGGBB"></feFlood>
<feComposite in2="dilated" operator="in"></feComposite>
<feMerge>
<feMergeNode></feMergeNode>
<feMergeNode in="SourceGraphic"></feMergeNode>
</feMerge>
</filter>
This works nicely, but only for one specific glow colour.
I would like to be able to have an arbitrary glow colour, in some way passing a variable in to the flood-color property.
I have tried using currentColor, but this seems to be the colour as it is when the filter is defined, not when it is applied.
I could define a filter for each colour (there will be a limited number of them) but it would be nicer - and certainly more space-efficient - to only need to define it once. Is this possible and if so how?

In the next example the flood-color is the current color. If you click the svg element you toggle the class "blue". The color of the svg element is red the color of the .blue is blue.
When you toggle the class blue the filter is changing the flood color.
test.addEventListener("click",()=>{
test.classList.toggle("blue")
})
svg {
border: 1px solid;
font-size: 40px;
text-anchor: middle;
dominant-baseline: middle;
width:100px;
color:red;
}
.blue{color:blue;}
<svg id="test" viewBox="0 0 100 70">
<filter id="outline">
<feMorphology in="SourceAlpha" operator="dilate" radius="2"></feMorphology>
<feGaussianBlur stdDeviation="1" result="dilated"></feGaussianBlur>
<feFlood style="flood-color: currentcolor"></feFlood>
<feComposite in2="dilated" operator="in"></feComposite>
<feMerge>
<feMergeNode></feMergeNode>
<feMergeNode in="SourceGraphic"></feMergeNode>
</feMerge>
</filter>
<text x="50" y="40" filter="url(#outline)">click</text>
</svg>

Related

Inset shadows on svg are just acting as an outline

I'm trying to add some inset shadows to my svg, to make it look more like this
Instead of like this
I tried adding an inset shadow to every path (filter="url(#inset-shadow)"), using the filter pattern I got from this answer and have listed below, but all it ends up doing is kind of giving each path an outline. You can check the code at this fiddle to see what I'm talking about.
<filter id="inset-shadow">
<feComponentTransfer in="SourceAlpha" result="inset-selection">
<feFuncA type="discrete" tableValues="0 1 1 1 1 1" />
</feComponentTransfer>
<feComponentTransfer in="SourceGraphic" result="original-no-fill">
<feFuncA type="discrete" tableValues="0 0 1" />
</feComponentTransfer>
<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>
<feComposite operator="in" in="inset-selection" in2="blur" result="inset-blur" />
<feComposite operator="over" in="original-no-fill" in2="inset-blur" />
</filter>
How can I add inset shadows to my shapes, to make my svg look more like the image at the top of this post?
The one you are using may have been failing because it was intended for transparent paths. I didn't spend much time working out what is wrong.
In any case, here's one I've created, that might be a bit easier to understand and tinker with.
<svg viewBox="0 0 200 200" width="400">
<defs>
<filter id="inset-shadow" x="-50%" y="-50%" width="200%" height="200%">
<!-- change this to desired inset blur colour -->
<feFlood x="0%" y="0%" width="100%" height="100%" flood-color="black" result="flood"/>
<!-- cut a hole out of the flood fill where out shape is -->
<feComposite operator="out" in="flood" in2="SourceAlpha" result="outside" />
<!-- stack blurs to get a better effect -->
<feGaussianBlur in="outside" result="blur" stdDeviation="5" />
<feGaussianBlur in="outside" result="blur2" stdDeviation="10" />
<feGaussianBlur in="outside" result="blur3" stdDeviation="15" />
<feMerge result="blur">
<feMergeNode in="blur" mode="normal" />
<feMergeNode in="blur2" mode="normal" />
<feMergeNode in="blur3" mode="normal" />
</feMerge>
<!-- clip the full blur against the shape to retain just the part inside our shape -->
<feComposite operator="in" in="blur" in2="SourceGraphic" result="inset-blur" />
<!-- blend with our original graphic to create the final result -->
<feBlend in="SourceGraphic" in2="inset-blur" mode="multiply"/>
</filter>
</defs>
<rect x="50" y="50" width="100" height="100" fill="linen" filter="url(#inset-shadow)"/>
</svg>
And here is a modified version of your fiddle with this filter applied.
You may want to tinker with the stdDeviation values to adjust the size of your inset blur.

SVG light and shadow a 3D feelling

I want to make an svg looks like it's on 3D by adding a small light in the top and left BORDER and a shadow in the bottom and right BORDER
something like this
#div1 {
background: #ddd;
}
#div1, #div2, #div3 {
width: 100px;
height: 100px;
position: relative;
}
#div2 {
box-shadow: inset -2px -2px 10px 1px #000;
position: absolute;
}
#div3 {
box-shadow: inset 2px 2px 14px 1px #fff;
position: absolute;
}
<div id="div1">
<div id="div2"></div>
<div id="div3"></div>
</div>
But I don't know how to do that with svg filter
<svg xmlns="http://www.w3.org/2000/svg" width="100%" height="1000">
<defs>
<filter id="filter1" x="0" y="0">
<feSpecularLighting result="specOut"
specularExponent="20" lighting-color="#bbbbbb">
<fePointLight x="-100" y="-100" z="600"/>
</feSpecularLighting>
<feComposite in="SourceGraphic" in2="specOut"
operator="arithmetic" k1="0" k2="1" k3="1" k4="0"/>
</filter>
</defs>
<path filter="url(#filter1)" fill="#fff" stroke="#000" d="M20,20 L220,20 L220,220 L20,220 L20,20 "></path>
</svg>
Help please and thanks
Firstly, you are trying to light a pure white rectangle with a dim white light. You aren't going to see anything.
If you make the rectangle darker, you'll start to see some effect.
<svg xmlns="http://www.w3.org/2000/svg" width="100%" height="300">
<defs>
<filter id="filter1" x="0" y="0">
<feSpecularLighting result="specOut"
specularExponent="20" lighting-color="#bbbbbb">
<fePointLight x="-100" y="-100" z="600"/>
</feSpecularLighting>
<feComposite in="SourceGraphic" in2="specOut"
operator="arithmetic" k1="0" k2="1" k3="1" k4="0"/>
</filter>
</defs>
<path filter="url(#filter1)" fill="#666" stroke="#000" d="M20,20 L220,20 L220,220 L20,220 L20,20 "></path>
</svg>
But in the above example we are only getting gradient of light over our rectangle. How do we make a sort-of bevel edge on the rectangle?
It is important to know that it is not really the RGB channels of an element that determine how the lighting filter components behave. The lighting components treat the alpha component of the colour as a bump map. Varying values of alpha become a topological map that effects the way the pixels are lit.
One way to create varying values of alpha is to use a gaussian blur filter. Here's what that looks like. Note that it is the alpha channel (SourceAlpha) of our shape that we are blurring.
<svg xmlns="http://www.w3.org/2000/svg" width="100%" height="300">
<defs>
<filter id="filter2">
<feGaussianBlur in="SourceAlpha" stdDeviation="8" result="blur1"/>
<feBlend in="SourceGraphic" in2="blur1" mode="multiply"/>
</filter>
</defs>
<path filter="url(#filter2)" fill="#666" stroke="#000" d="M20,20 L220,20 L220,220 L20,220 L20,20 "></path>
</svg>
Now if use that blurred alpha channel, we get something close to what you are after.
You can fiddle with the blur, the lighting filter values, and the feComposite values to adjust the effect.
Note that I've also switched to using an feDistantLight here. I think it is more appropriate for this purpose.
<svg xmlns="http://www.w3.org/2000/svg" width="100%" height="300">
<defs>
<filter id="filter2">
<feGaussianBlur in="SourceAlpha" stdDeviation="8" result="blur1"/>
<feSpecularLighting result="specOut" in="blur1" specularConstant="1.2" specularExponent="12" lighting-color="#fff">
<feDistantLight azimuth="225" elevation="45"/>
</feSpecularLighting>
<feComposite in="SourceGraphic" in2="specOut" operator="arithmetic" k1="0" k2="1" k3="1" k4="0"/>
</filter>
</defs>
<path filter="url(#filter2)" fill="#666" stroke="#000" d="M20,20 L220,20 L220,220 L20,220 L20,20 "></path>
</svg>
Update
To handle the situation where shapes overlap (see comments), then you will need to clip away any parts of the blur that is outside the shape. You can do that with another feComposite operation.
<svg xmlns="http://www.w3.org/2000/svg" width="100%" height="300">
<defs>
<filter id="filter2">
<feGaussianBlur in="SourceAlpha" stdDeviation="8" result="blur1"/>
<feSpecularLighting result="specOut" in="blur1" specularConstant="1.2" specularExponent="12" lighting-color="#fff">
<feDistantLight azimuth="225" elevation="45"/>
</feSpecularLighting>
<feComposite in="SourceGraphic" in2="specOut" operator="arithmetic" k1="0" k2="1" k3="1" k4="0" result="result"/>
<feComposite operator="atop" in2="SourceGraphic"/>
</filter>
</defs>
<path filter="url(#filter2)" fill="#666" stroke="#000" d="M20,20 L220,20 L220,220 L20,220 L20,20 "></path>
<path filter="url(#filter2)" fill="#666" stroke="#000" d="M40,40 L200,40 L200,110 L40,110 L40,40 "></path>
<path filter="url(#filter2)" fill="#666" stroke="#000" d="M40,120 L200,120 L200,200 L40,200 L40,120 "></path>
</svg>

SVG Long Shadow

So I've got this svg of a code symbol on my website and I want to add a long shadow to the svg. I've tried using filter: drop-shadow() but that only allows for one offset, I'm looking for the same feature as box-shadow where I can add multiple offsets to make a long shadow.
I'm using Sass for my styling and have made a function that makes long shadows for boxes and then is called with a mixin but unfortunately I can't find how to do the same with svg.
Anyone got any work arounds?
Not entirely sure what you mean by a long shadow - but I'm assuming that you're looking for an extrusion? You can do that by doing multiple offsets and in a SVG filter - and then referencing that filter from CSS.
.icon {
width: 50px;
height: 50px;
filter: url(#long-shadow);
}
<img class="icon" src="https://material.io/tools/icons/static/ic_icons_192px_light.svg">
<svg width="0px" height="0px">
<defs>
<filter id="long-shadow" x="0%" width="130%" y="0%" height="130%">
<feOffset dx="1" dy="1" result="layer1" in="SourceAlpha"/>
<feOffset dx="1" dy="1" result="layer2" />
<feOffset dx="1" dy="1" result="layer3" />
<feOffset dx="1" dy="1" result="layer4" />
<feOffset dx="1" dy="1" result="layer5" />
<feOffset dx="1" dy="1" result="layer6" />
<feOffset dx="1" dy="1" result="layer7" />
<feMerge>
<feMergeNode in="layer1"/>
<feMergeNode in="layer2"/>
<feMergeNode in="layer3"/>
<feMergeNode in="layer4"/>
<feMergeNode in="layer5"/>
<feMergeNode in="layer6"/>
<feMergeNode in="layer7"/>
<feMergeNode in="SourceGraphic"/>
</feMerge>
</filter>
</defs>
</svg>

SVG Filter tag on hover

I am trying to animate an SVG which I managed to do via this filter:
<defs>
<filter id="green-fill" x="0%" y="0%">
<feFlood flood-color="#fff"/>
<feOffset dx="85">
<animate attributeName="dx" from="0" to="85" dur="5s"/>
</feOffset>
<feComposite operator="in" in2="SourceGraphic"/>
<feComposite operator="over" in2="SourceGraphic"/>
</filter>
</defs>
my real problem comes when I am trying to trigger the animation on hover state,
it seems that the animation is taking place when the page is load.
I tried by adding the filter on hover with pure css but no chance:
.item:hover .svg-fill path{ filter:url(#green-fill); }
Also I find that can be done with Javascript but again didn't had any success.
here is the codepen example:CodePen
you can set begin="indefinite" on your animate element, and then call the .beginElement() function on that element whenever you want to start it.
function startAnimation() {
anim.beginElement()
}
path {
filter: url(#green-fill);
}
<svg data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 30 25.54" width="100px" height="100px">
<defs>
<filter id="green-fill" x="0%" y="0%">
<feFlood flood-color="#fff" />
<feOffset dx="0">
<animate id="anim" attributeName="dx" from="0" to="85" dur="5s" begin="indefinite" />
</feOffset>
<feComposite operator="in" in2="SourceGraphic" />
<feComposite operator="over" in2="SourceGraphic" />
</filter>
</defs>
<path fill="#ea0097" d="M12.41,4.67a8,8,0,0,0-7.89,8.18A8,8,0,0,0,12.41,21a7.64,7.64,0,0,0,5-1.83l6.22-6.34L17.39,6.5a7.73,7.73,0,0,0-5-1.83m0,20.95A12.61,12.61,0,0,1,0,12.85,12.61,12.61,0,0,1,12.41.07a12.21,12.21,0,0,1,8,3l0.14,0.13L30,12.85l-9.62,9.79a12.23,12.23,0,0,1-8,3"
/>
</svg>
<button onclick="startAnimation()">start Animation</button>

Is it possible to animate the opacity of a SVG filter with CSS code?

I have an svg image that works as a button. As you hover over the button the svg fill changes to a different color, but I also wish the inner shadow filter I created to appear as well. That is, to change from 0% opacity to 100%.
I was able to change the fill color with css transitions. Is it possible to change the opacity of the filter too with css?
Here's the SVG code:
<svg version="1.1" id="Capa_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
width="36.25px" height="23px" viewBox="5 -5 36.25 23" enable-background="new 0 0 36.25 23" xml:space="preserve">
<!--Definiciones del Filtro-->
<filter id="sombra" x="-50%" y="-50%" width="200%" height="200%">
<feComponentTransfer in="SourceAlpha">
<feFuncA type="table" tableValues="1 0" />
</feComponentTransfer>
<feGaussianBlur stdDeviation="1"/>
<feOffset dx="-1" dy="-1" result="offsetblur"/>
<feFlood flood-color="rgb(20, 0, 0)" result="color"/>
<feComposite in2="offsetblur" operator="in"/>
<feComposite in2="SourceAlpha" operator="in" />
<feMerge>
<feMergeNode in="SourceGraphic" />
<feMergeNode />
</feMerge>
</filter>
<g filter="url(#sombra)">
<polygon points="23.671,8.625 23.671,4.657 21.786,4.657 21.786,7.513 18.255,5.433 18.194,5.398 18.058,5.398
4.724,13.249 5.502,14.57 7.862,13.179 7.862,18.343 9.396,18.343 9.396,12.276 18.128,7.138 26.854,12.276 26.854,18.343
28.388,18.343 28.388,13.179 30.75,14.57 31.526,13.249 "/>
<rect x="15.289" y="11.553" width="2.44" height="2.444"/>
<rect x="18.523" y="11.553" width="2.438" height="2.444"/>
<rect x="15.289" y="14.656" width="2.44" height="2.439"/>
<rect x="18.523" y="14.656" width="2.438" height="2.439"/>
</g>
</svg>
Here is the CSS:
#Capa_1{
fill:#FFFFFF;
-webkit-transition: fill 0.3s;
}
#Capa_1:hover{
fill:#8A653B;
}
Thanks in advance...!
After hours of searching somebody had already answered this question.
Here's the workaround:
Using CSS, is there a way to change the opacity of an SVG's <filter> element?
It works around pretty well on the current versions of Firefox and Chrome. SVG animations don't seem to work well in IE though, but the filter opacity did change however.
Thanks!
Yes MDN doc has a working draft of the animatable filter.
but not a lot of support is currently available :(
And on svg elements it only works in firefox.
Here is a fiddle for the fire fox users: Fiddle
Applying an svg filter: filter: url(#filterid)
Applying an svg opacity filter: filter: opacity(50%);
And combined: filter: url(#filterid) opacity(50%);