I try to create a rect with 3 borders around using filters.
The result should look like this:
but my result looks like this:
code:
<svg width=1000 height=1000 >
<rect width=300 height=50 rx=25 x=100 y=100 filter="url(#filter)" fill="white"></rect>
<filter id="filter">
<feFlood flood-color="RGBA(173, 15, 91, 1.00)" result="fill1"></feFlood>
<feMorphology in="SourceAlpha" operator="dilate" radius="8" result="radius1"></feMorphology>
<feComposite in="fill1" in2="radius1" operator="in" result="compose1"></feComposite>
<feFlood flood-color="RGBA(217, 145, 180, 1.00)" result="fill2"></feFlood>
<feMorphology in="SourceAlpha" operator="dilate" radius="16" result="radius2"></feMorphology>
<feComposite in="fill2" in2="radius2" operator="in" result="compose2"></feComposite>
<feFlood flood-color="RGBA(247, 231, 239, 1.00)" result="fill3"></feFlood>
<feMorphology in="SourceAlpha" operator="dilate" radius="24" result="radius3"></feMorphology>
<feComposite in="fill3" in2="radius3" operator="in" result="compose3"></feComposite>
<feMerge result="a452afbd-5e3f-4c25-abcf-3c77051dd340">
<feMergeNode in="compose3"></feMergeNode>
<feMergeNode in="compose2"></feMergeNode>
<feMergeNode in="compose1"></feMergeNode>
<feMergeNode in="SourceGraphic"></feMergeNode>
</feMerge>
</filter>
</svg>
any idea how to get this right using filters.
If you want to retain the shape, you can't use feMorphology because it uses a square evenly weighted kernel. Instead you have to use a blur and an alpha boost (aka the gooey effect). (Also need to increase the filter region)
<svg width="1000px" height="1000px" >
<rect width=300 height=50 rx=25 x=100 y=100 filter="url(#filter)" fill="white"></rect>
<filter id="filter" x="-50%" y="-100%" height="400%" width="200%" color-interpolation-filters="sRGB">
<feFlood flood-color="RGBA(173, 15, 91, 1.00)" result="fill1"/>
<feGaussianBlur stdDeviation="8" in="SourceGraphic"/>
<feColorMatrix type="matrix" values="1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 20 -2" result="radius1"/>
<feComposite in="fill1" in2="radius1" operator="in" result="compose1"/>
<feFlood flood-color="RGBA(217, 145, 180, 1.00)" result="fill2"/>
<feGaussianBlur stdDeviation="8" in="compose1" />
<feColorMatrix type="matrix" values="1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 20 -2" result="radius2"/>
<feComposite in="fill2" in2="radius2" operator="in" result="compose2"/>
<feFlood flood-color="RGBA(247, 231, 239, 1.00)" result="fill3"/>
<feGaussianBlur stdDeviation="8" in="compose2"/>
<feColorMatrix type="matrix" values="1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 20 -2" result="radius3"/>
<feComposite in="fill3" in2="radius3" operator="in" result="compose3"/>
<feMerge result="a452afbd-5e3f-4c25-abcf-3c77051dd340">
<feMergeNode in="compose3"></feMergeNode>
<feMergeNode in="compose2"></feMergeNode>
<feMergeNode in="compose1"></feMergeNode>
<feMergeNode in="SourceGraphic"></feMergeNode>
</feMerge>
</filter>
</svg>
You just neede to increase the filter bounds so they extend far enough outside the object being filtered.
The defaults are an additional 10% in all directions but that's not enough for your use case.
<svg width=1000 height=1000 >
<rect width=300 height=50 rx=25 x=100 y=100 filter="url(#filter)" fill="white"></rect>
<filter id="filter" y="-70%" height="240%">
<feFlood flood-color="RGBA(173, 15, 91, 1.00)" result="fill1"></feFlood>
<feMorphology in="SourceAlpha" operator="dilate" radius="8" result="radius1"></feMorphology>
<feComposite in="fill1" in2="radius1" operator="in" result="compose1"></feComposite>
<feFlood flood-color="RGBA(217, 145, 180, 1.00)" result="fill2"></feFlood>
<feMorphology in="SourceAlpha" operator="dilate" radius="16" result="radius2"></feMorphology>
<feComposite in="fill2" in2="radius2" operator="in" result="compose2"></feComposite>
<feFlood flood-color="RGBA(247, 231, 239, 1.00)" result="fill3"></feFlood>
<feMorphology in="SourceAlpha" operator="dilate" radius="24" result="radius3"></feMorphology>
<feComposite in="fill3" in2="radius3" operator="in" result="compose3"></feComposite>
<feMerge result="a452afbd-5e3f-4c25-abcf-3c77051dd340">
<feMergeNode in="compose3"></feMergeNode>
<feMergeNode in="compose2"></feMergeNode>
<feMergeNode in="compose1"></feMergeNode>
<feMergeNode in="SourceGraphic"></feMergeNode>
</feMerge>
</filter>
</svg>
Couldn't you just draw this with separate paths instead?
<svg width="320" height="120" viewBox="0 0 320 120">
<rect x="10" y="10" width="300" height="100" rx="50" ry="50" fill="none" stroke="#fad" stroke-width="10"/>
<rect x="20" y="20" width="280" height="80" rx="40" ry="40" fill="none" stroke="#b58" stroke-width="10"/>
<rect x="30" y="30" width="260" height="60" rx="30" ry="30" fill="none" stroke="#603" stroke-width="10"/>
</svg>
Related
I am trying to apply multiple dropshadow effects - each with a different colour using SVG, I can position the elements perfectly but I cannot figure out how to colour them independently. The following gets me 90% there but both shadows are the same colour:
<svg className={style.svg} viewBox={`0 0 150 150`}>
<defs>
<filter id="drop-shadow" x="-100%" y="-100%" width="300%" height="300%">
<feGaussianBlur in="SourceAlpha" stdDeviation="2" />
<feColorMatrix
in="offOut"
type="matrix"
values="0.2 0 0 0 0 0 0.2 0 0 1 0 0 0.2 0 0 0 0 0 1 0"
/>
<feOffset dx="-5" dy="5" result="offsetblur" />
<feOffset dx="10" dy="-10" result="offsetblur2" in="blur" />
<feComponentTransfer result="shadow1" in="offsetblur">
<feFuncA type="linear" slope="0.3" />
</feComponentTransfer>
<feComponentTransfer result="shadow2" in="offsetblur2">
<feFuncA type="linear" slope="0.3" />
</feComponentTransfer>
<feMerge>
<feMergeNode in="shadow1" />
<feMergeNode in="shadow2" />
<feMergeNode in="SourceGraphic" />
</feMerge>
</filter>
</defs>
<circle
stroke="#ff00ff"
fill="#ff00ff"
cx=75
cy=75
r=55
strokeWidth=15
filter="url(#drop-shadow)"
/>
</svg>
CODEPEN
I copied this from one of the recommended answers here on stack overflow but I cant tailor it for my specific purposes. How do I colour each shadow independently?
Thanks in advance.
It's a bit more performant to use color matrix vs. component transfer if you can - browsers can do color matrix on the GPU. You can also get rid of a few steps and specify colors directly using the fifth column. SourceAlpha has color channels set to zero/black - so applying a multiplier them doesn't do anything.
<svg className={style.svg} viewBox={`0 0 150 150`}>
<defs>
<filter id="drop-shadow" x="-100%" y="-100%" width="300%" height="300%">
<feGaussianBlur in="SourceAlpha" stdDeviation="2" />
<feColorMatrix
type="matrix"
values="0 0 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 0 0.3 0"
/>
<feOffset dx="-5" dy="5" result="shadow1" />
<feOffset dx="10" dy="-10" result="offsetblur2" />
<feColorMatrix type="matrix" result="shadow2"
values="0 0 0 0 1 0 0 0 0 0 0 0 0 0 0 0 0 0 1 0"
/>
</feComponentTransfer>
<feMerge>
<feMergeNode in="shadow1" />
<feMergeNode in="shadow2" />
<feMergeNode in="SourceGraphic" />
</feMerge>
</filter>
</defs>
<circle
stroke="#ff00ff"
fill="#ff00ff"
cx=75
cy=75
r=55
strokeWidth=15
filter="url(#drop-shadow)"
/>
</svg>
I used the feFuncR/G/B tag to change the filter colour like so:
<defs>
<filter id="drop-shadow" x="-100%" y="-100%" width="300%" height="300%">
<feGaussianBlur in="SourceAlpha" stdDeviation="5" />
<feOffset dx="-15" dy="15" result="offsetblur" />
<feOffset dx="25" dy="-25" result="offsetblur2" in="blur" />
<feComponentTransfer result="shadow1" in="offsetblur">
<feFuncA type="linear" slope="0.3" />
</feComponentTransfer>
<feComponentTransfer result="shadow2" in="offsetblur2">
<feFuncA type="linear" slope="0.2" />
<feFuncR type="linear" slope="2.0" intercept="1" />
<feFuncG type="linear" slope="2.0" intercept="1" />
<feFuncB type="linear" slope="2.0" intercept="1" />
</feComponentTransfer>
<feMerge>
<feMergeNode in="shadow1" />
<feMergeNode in="shadow2" />
<feMergeNode in="SourceGraphic" />
</feMerge>
</filter>
</defs>
Posting the answer so that it might help others in the same position.
Assuming I have a circle shape (or any shape) in SVG with semi-transparent black fill (or any semi-transparent color):
<circle r="50" fill="rgba(0, 0, 0, 0.2)" />
How can I drop a configurable (color, blur, position) inset shadow whose transparency is independent of the shape fill?
Note: I do not know anything about the background beforehand, the SVG must be really transparent, not faked.
Instead of drawing a circle you draw a path representing a rectangle with a hole and apply the filter to this path.
What you see as a golden circle is in fact a rectangle drawn behind the shaded path.
<svg viewBox="-100 -100 200 200" width="300">
<defs>
<filter id="f">
<feGaussianBlur in="SourceAlpha" stdDeviation="5" result="desenfoque"></feGaussianBlur>
<feOffset in="desenfoque" dx="3" dy="3" result="sombra"></feOffset>
<feMerge>
<feMergeNode in="sombra"></feMergeNode>
<feMergeNode in="SourceGraphic"></feMergeNode>
</feMerge>
</filter>
</defs>
<rect x="-100" y="-100" width="200" height="200" fill="gold" />
<path fill="yellow" d="M-100,-100v200h200v-200h-200M50,0A50,50 0 0 1 -50,0A50,50 0 0 1 50,0z" filter="url(#f)" />
</svg>
Easiest way to do this is to do an inset-shadow - first resetting the source graphic's color to black/fully opaque.
<svg width="800px" height="600px" viewBox="0 0 400 300">
<defs>
<filter id="inset-shadow">
<feColorMatrix in="SourceGraphic" type="matrix" values="0 0 0 0 0
0 0 0 0 0
0 0 0 0 0
0 0 0 100 0" result="opaque-source"/>
<feGaussianBlur stdDeviation="5"/>
<feOffset dy="10"/>
<feComposite operator="xor" in2="opaque-source"/>
<feComposite operator="in" in2="opaque-source"/>
<feComposite operator="over" in2="SourceGraphic"/>
</filter>
</defs>
<circle filter="url(#inset-shadow)" cx="100" cy="100" r="50" fill="rgba(0, 0, 0, 0.2)" />
<svg>
Or you can use a lighting effect to do this - more complicated, and performance can be pretty mixed.
<svg width="800px" height="600px" viewBox="0 0 400 300">
<defs>
<filter id="top-light">
<feColorMatrix type="matrix" values="1 0 0 0 0
0 1 0 0 0
0 0 1 0 0
0 0 0 100 0"/>
<feGaussianBlur stdDeviation="2"/>
<feComposite operator="in" in2="SourceGraphic"/>
<feDiffuseLighting surfaceScale="200" diffuseConstant="1" kernelUnitLength="1" lighting-color="white" result="lightmap">
<fePointLight x="100" y="0" z="10" />
</feDiffuseLighting>
<feGaussianBlur stdDeviation="2"/>
<feColorMatrix type="luminanceToAlpha" />
// Insert another color matrix in here to recolor the shadow
<feComposite operator="in" in2="SourceGraphic"/>
<feComposite operator="over" in2="SourceGraphic"/>
</filter>
</defs>
<circle filter="url(#top-light)" cx="100" cy="100" r="50" fill="rgba(0, 0, 0, 0.2)" />
<svg>
Is it possible to render a border of a SVG rect to emulate an outset?
with CSS?
with filters?
NB: It has to be a single scalable simple rect object, so I don't want to build it with paths / composite objects.
It's possible to do this with a filter. You do it by drawing a masking shape in the SVG and doing the stroking in the filter.
<svg width="600px" height="800px" color-interpolation-filters="sRGB">
<filter id="rect-and-stroke" x="0%" y="0%" width="100%" height="100%">
<feFlood flood-color="red" result="red-stroke"/>
<feFlood flood-color="orange" result="orange-stroke"/>
<feFlood x="10" y="10" width="180" height="80" flood-color="yellow" result="yellow-field"/>
<feComposite operator="in" in2="SourceGraphic" in="red-stroke" result="red-partial"/>
<feComposite operator="out" in2="SourceGraphic" in="orange-stroke" result="orange-partial"/>
<feMerge>
<feMergeNode in="red-partial" />
<feMergeNode in="orange-partial" />
<feMergeNode in="yellow-field" />
</feMerge>
</filter>
<path filter="url(#rect-and-stroke)" fill="black" d="M 0 0 L 10 10 180 80 200 100 0 100Z"/>
</svg>
I see how to use feComponentTransfer to adjust RGBA values using a table and tableValues.
But is there a way to adjust the R, G, or B values using alpha values as input (for example)?
Why? Say I composite many translucent shapes, resulting in different opacities where these shapes overlap. I would like to use these opacity values to adjust the RGB using a lookup table based on the opacity of each pixel.
For example, in this image the ellipse, squares, and text are drawn with alpha 0.1. Overlapping regions have higher alpha values.
How would I map every pixel in this image with alpha 0.1 to rgba(255,0,0,1) and everywhere with alpha 0.2 to rgba(0,255,0,1) (and interpolate the replacement colors between those two alpha values on the anti-aliased edges)? More colors would be assigned to all other alpha values up to 1.
Easy to make such a lookup table.
But how to apply to image with a svg filter?
You can achieve it, but it is a multistep process, and you'll have to see about performance and maintainability yourself.
My example code divides the opacity values into four equally-spaced ranges: 0.2...0.4...0.6...0.8...1. The range 0...0.2 is mapped to transparency.
For each of the four colored ranges you need to combine three filter primitives:
pick up SourceAlpha as input, and apply <feComponentTransfer> to the alpha channel with type discrete such that only one range is mapped to opacity="1"
fill the filter area with <feFlood> in the assigned color
combine the first and second result with <feComposite> and operator="in", so that only the mapped values are colored
Then, merge all the different monochromatic parts together with <feMerge>.
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="400" height="200">
<linearGradient id="sourceGradient" x1="0" x2="1" y1="0" y2="0">
<stop style="stop-color:#000000;stop-opacity:0" offset="0" />
<stop style="stop-color:#000000;stop-opacity:1" offset="1" />
</linearGradient>
<filter id="filter">
<feComponentTransfer in="SourceAlpha" result="step1">
<feFuncA type="discrete" tableValues="0 1 0 0 0"/>
</feComponentTransfer>
<feFlood flood-color="rgb(255,0,0)" />
<feComposite operator="in" in2="step1" result="color1" />
<feComponentTransfer in="SourceAlpha" result="step2">
<feFuncA type="discrete" tableValues="0 0 1 0 0"/>
</feComponentTransfer>
<feFlood flood-color="rgb(255,255,0)" />
<feComposite operator="in" in2="step2" result="color2" />
<feComponentTransfer in="SourceAlpha" result="step3">
<feFuncA type="discrete" tableValues="0 0 0 1 0"/>
</feComponentTransfer>
<feFlood flood-color="rgb(0,255,0)" />
<feComposite operator="in" in2="step3" result="color3" />
<feComponentTransfer in="SourceAlpha" result="step4">
<feFuncA type="discrete" tableValues="0 0 0 0 1"/>
</feComponentTransfer>
<feFlood flood-color="rgb(0,0,255)" />
<feComposite operator="in" in2="step4" result="color4" />
<feMerge>
<feMergeNode in="color1" />
<feMergeNode in="color2" />
<feMergeNode in="color3" />
<feMergeNode in="color4" />
</feMerge>
</filter>
<rect fill="url(#sourceGradient)" x="50" y="25" width="300" height="50" id="rect" />
<use xlink:href="#rect" y="100" filter="url(#filter)" />
</svg>
interpolate the replacement colors between those two alpha values on the anti-aliased edges
I am not sure I understand that, but type="table" for component transfer results in a continuous scale. The result after merge has opacities below 1 in most places, so you would need some final steps to ramp that up again.
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="400" height="200">
<linearGradient id="sourceGradient" x1="0" x2="1" y1="0" y2="0">
<stop style="stop-color:#000000;stop-opacity:0" offset="0" />
<stop style="stop-color:#000000;stop-opacity:1" offset="1" />
</linearGradient>
<filter id="filter" x="0" y="0" width="1" height="1">
<feComponentTransfer in="SourceAlpha" result="step1">
<feFuncA type="table" tableValues="0 1 0 0 0"/>
</feComponentTransfer>
<feFlood flood-color="rgb(255,0,0)" />
<feComposite operator="in" in2="step1" result="color1" />
<feComponentTransfer in="SourceAlpha" result="step2">
<feFuncA type="table" tableValues="0 0 1 0 0"/>
</feComponentTransfer>
<feFlood flood-color="rgb(255,255,0)" />
<feComposite operator="in" in2="step2" result="color2" />
<feComponentTransfer in="SourceAlpha" result="step3">
<feFuncA type="table" tableValues="0 0 0 1 0"/>
</feComponentTransfer>
<feFlood flood-color="rgb(0,255,0)" />
<feComposite operator="in" in2="step3" result="color3" />
<feComponentTransfer in="SourceAlpha" result="step4">
<feFuncA type="table" tableValues="0 0 0 0 1"/>
</feComponentTransfer>
<feFlood flood-color="rgb(0,0,255)" />
<feComposite operator="in" in2="step4" result="color4" />
<feMerge result="merged">
<feMergeNode in="color1" />
<feMergeNode in="color2" />
<feMergeNode in="color3" />
<feMergeNode in="color4" />
</feMerge>
<feComponentTransfer in="SourceAlpha" result="capped">
<feFuncA type="discrete" tableValues="0 1 1 1 1"/>
</feComponentTransfer>
<feComposite operator="atop" in="merged" in2="capped" />
</filter>
<rect fill="url(#sourceGradient)" x="50" y="25" width="300" height="50" id="rect" />
<use xlink:href="#rect" y="100" filter="url(#filter)" />
</svg>
I have the following svg with a filter that works in Chrome but on ios only part of it works. Not sure if I'm doing something wrong or if ios doesn't fully support it.
<svg height="80" width="500" xmlns="http://www.w3.org/2000/svg">
<defs>
<circle cx="3" cy="3" r="2" id="circle" fill="#FFFFFF"></circle>
<filter height="100%" id="filter">
<feMorphology in="SourceAlpha" operator="dilate" radius="2.5"
result="MORPH1"></feMorphology>
<feColorMatrix in="MORPH1" result="GREYED" type="matrix"
values="0.8 0 0 0 0 0 0.8 0 0 0 0 0 0.8 0 0 0 0 0 0.5 0">
</feColorMatrix>
<feMorphology in="SourceAlpha" operator="dilate" radius="1.5"
result="MORPH2"></feMorphology>
<feColorMatrix in="MORPH2" result="WHITENED" type="matrix"
values="-1 0 0 1 0, 0 -1 0 1 0, 0 0 -1 1 0, 0 0 0 0.8 0">
</feColorMatrix>
<feImage height="2" width="2" xlink:href="#circle">
</feImage>
<feTile result="3dot"></feTile>
<feComposite in="3dot" in2="SourceGraphic" operator="in"
result="comp"></feComposite>
<feMerge>
<feMergeNode in="GREYED"></feMergeNode>
<feMergeNode in="WHITENED"></feMergeNode>
<feMergeNode in="SourceGraphic"></feMergeNode>
<feMergeNode in="comp"></feMergeNode>
</feMerge>
</filter>
</defs>
<text text-anchor="start" alignment-baseline="hanging" font-size="48"
x="20" y="20" fill="#803cac" stroke="#000000" stroke-width="1"
filter="url(#filter)">TEXT HERE</text>
</svg>
The result should look like
but on ios the dots are not displaying so everything apart from this part works
<feImage height="2" width="2" xlink:href="#circle">
</feImage>
<feTile result="3dot"></feTile>
<feComposite in="3dot" in2="SourceGraphic" operator="in"
result="comp"></feComposite>
This is a straight up bug in iOS/Safari (didn't test regular safari). The workaround is to "pre-tile" the fill using a filled shape. (Note Firefox doesn't support object references in feImage - so you'd need to use an inline data:uri for cross browser compat)
<svg height="80" width="500" xmlns="http://www.w3.org/2000/svg">
<defs>
<pattern id="circle-fill" width="2" height="2" patternUnits="userSpaceOnUse">
<circle cx="3" cy="3" r="2" id="circle" fill="white"/>
</pattern>
<rect x="0" y="0" id="filled-rect" width="100%" height="100%" fill="url(#circle-fill)"/>
<filter height="100%" id="filter">
<feMorphology in="SourceAlpha" operator="dilate" radius="2.5"
result="MORPH1"/>
<feColorMatrix in="MORPH1" result="GREYED" type="matrix"
values="0.8 0 0 0 0 0 0.8 0 0 0 0 0 0.8 0 0 0 0 0 0.5 0"/>
<feMorphology in="SourceAlpha" operator="dilate" radius="1.5"
result="MORPH2"/>
<feColorMatrix in="MORPH2" result="WHITENED" type="matrix"
values="-1 0 0 1 0 0 -1 0 1 0 0 0 -1 1 0, 0 0 0 0.8 0"/>
<feImage height="80" width="500" xlink:href="#filled-rect"/>
<feComposite in="3dot" in2="SourceGraphic" operator="in"
result="comp"/>
<feMerge>
<feMergeNode in="GREYED"/>
<feMergeNode in="WHITENED"/>
<feMergeNode in="SourceGraphic"/>
<feMergeNode in="comp"/>
</feMerge>
</filter>
</defs>
<text text-anchor="start" alignment-baseline="hanging" font-size="48"
x="20" y="20" fill="#803cac" stroke="#000000" stroke-width="1"
filter="url(#filter)">TEXT HERE</text>
</svg>