Make Blend Modes in SVG actually work? - svg

I've many times done the following:
<defs>
<filter id="screen">
<feBlend mode="screen" in2="BackgroundImage"/>
</filter>
</defs>
But when I, inside a shape, write "filter="url(#screen)", my shape disappears.
I've tried it in every browser (Safari, Chrome, Firefox, FfxNightly). What am I doing wrong?
It would help if someone could give me an example that they know works
Thanks

You should try Opera and see the difference. It seems, Opera is the only browser that currently implements this more or less correctly. The specs for <feBlend> give an example:
<?xml version="1.0"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN"
"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg width="5cm" height="5cm" viewBox="0 0 500 500"
xmlns="http://www.w3.org/2000/svg" version="1.1">
<title>Example feBlend - Examples of feBlend modes</title>
<desc>Five text strings blended into a gradient,
with one text string for each of the five feBlend modes.</desc>
<defs>
<linearGradient id="MyGradient" gradientUnits="userSpaceOnUse"
x1="100" y1="0" x2="300" y2="0">
<stop offset="0" stop-color="#000000" />
<stop offset=".33" stop-color="#ffffff" />
<stop offset=".67" stop-color="#ff0000" />
<stop offset="1" stop-color="#808080" />
</linearGradient>
<filter id="Normal">
<feBlend mode="normal" in2="BackgroundImage" in="SourceGraphic"/>
</filter>
<filter id="Multiply">
<feBlend mode="multiply" in2="BackgroundImage" in="SourceGraphic"/>
</filter>
<filter id="Screen">
<feBlend mode="screen" in2="BackgroundImage" in="SourceGraphic"/>
</filter>
<filter id="Darken">
<feBlend mode="darken" in2="BackgroundImage" in="SourceGraphic"/>
</filter>
<filter id="Lighten">
<feBlend mode="lighten" in2="BackgroundImage" in="SourceGraphic"/>
</filter>
</defs>
<rect fill="none" stroke="blue"
x="1" y="1" width="498" height="498"/>
<g enable-background="new" >
<rect x="100" y="20" width="300" height="460" fill="url(#MyGradient)" />
<g font-family="Verdana" font-size="75" fill="#888888" fill-opacity=".6" >
<text x="50" y="90" filter="url(#Normal)" >Normal</text>
<text x="50" y="180" filter="url(#Multiply)" >Multiply</text>
<text x="50" y="270" filter="url(#Screen)" >Screen</text>
<text x="50" y="360" filter="url(#Darken)" >Darken</text>
<text x="50" y="450" filter="url(#Lighten)" >Lighten</text>
</g>
</g>
</svg>
This example should look like this if rendered correctly:
It actually looks like this in your browser:
In my Opera it looks like this:
I.e., not entirely perfect, there are problems with mode="lighten".

Related

Masking SVG with blur produces ugly results

I have rectangles / paths as rectangles with a glow effect. That works well as long as I do not mask the inner part (i.e. hiding either the filling or the glowing inside of the rectangles). Masking part of the objects produces some ugly effect of the previously smooth glow.
So, applying the mask seems to render the previous "image" somehow. Is there a way to avoid this? If not, are there alternatives?
<svg id="button-glow" width="400" height="400" viewBox="0 0 400 400" xmlns="http://www.w3.org/2000/svg" version="1.1">
<defs>
<filter id="cyan-glow" x="-5000%" y="-5000%" width="10000%" height="10000%">
<feFlood result="flood" flood-color="#00e4ff" flood-opacity="1"></feFlood>
<feComposite in="flood" result="mask" in2="SourceGraphic" operator="in"></feComposite>
<feMorphology in="mask" result="dilated" operator="dilate" radius="2"></feMorphology>
<feGaussianBlur in="dilated" result="blurred" stdDeviation="5"></feGaussianBlur>
<feMerge>
<feMergeNode in="blurred"></feMergeNode>
<feMergeNode in="SourceGraphic"></feMergeNode>
</feMerge>
</filter>
<mask id="Mask1">
<rect x="-100" y="-100" width="250" height="250" fill="white" />
<rect x="3" y="3" width="34" height="34" fill="black" />
</mask>
<mask id="Mask2">
<rect x="-100" y="-100" width="250" height="250" fill="white" />
<rect x="3" y="3" width="34" height="34" fill="black" />
</mask>
</defs>
<g transform="translate(20,20)">
<rect x="0" y="0" width="40" height="40" fill="#00e4ff"/>
<rect transform="translate(60,0)" x="0" y="0" width="40" height="40"
fill="#00e4ff" style="filter:url(#cyan-glow)"/>
<rect transform="translate(120,0)" x="0" y="0" width="40" height="40"
fill="#00e4ff" style="filter:url(#cyan-glow)" mask="url(#Mask1)"/>
<path transform="translate(0,60)" d="M0,0 L40,0 L40,40 L0,40 z" stroke="#00e4ff"
stroke-width="3" fill="none"/>
<path transform="translate(60,60)" d="M0,0 L40,0 L40,40 L0,40 z" stroke="#00e4ff"
stroke-width="3" style="filter:url(#cyan-glow)" fill="none"/>
<path transform="translate(120,60)" d="M0,0 L40,0 L40,40 L0,40 z" stroke="#00e4ff"
stroke-width="3" style="filter:url(#cyan-glow)" fill="none" mask="url(#Mask2)"/>
</g>
</svg>
Just make the masks bigger.
You'd also be better off making the filter smaller. The huge filter size is why your SVG is very slow to render.
<svg id="button-glow" width="400" height="400" viewBox="0 0 400 400" xmlns="http://www.w3.org/2000/svg" version="1.1">
<defs>
<filter id="cyan-glow" x="-5000%" y="-5000%" width="10000%" height="10000%">
<feFlood result="flood" flood-color="#00e4ff" flood-opacity="1"></feFlood>
<feComposite in="flood" result="mask" in2="SourceGraphic" operator="in"></feComposite>
<feMorphology in="mask" result="dilated" operator="dilate" radius="2"></feMorphology>
<feGaussianBlur in="dilated" result="blurred" stdDeviation="5"></feGaussianBlur>
<feMerge>
<feMergeNode in="blurred"></feMergeNode>
<feMergeNode in="SourceGraphic"></feMergeNode>
</feMerge>
</filter>
<mask id="Mask1" x="-40%" y="-40%" width="180%" height="180%">
<rect x="-100" y="-100" width="250" height="250" fill="white" />
<rect x="3" y="3" width="34" height="34" fill="black" />
</mask>
<mask id="Mask2" x="-40%" y="-40%" width="180%" height="180%">
<rect x="-100" y="-100" width="250" height="250" fill="white" />
<rect x="3" y="3" width="34" height="34" fill="black" />
</mask>
</defs>
<g transform="translate(20,20)">
<rect x="0" y="0" width="40" height="40" fill="#00e4ff"/>
<rect transform="translate(60,0)" x="0" y="0" width="40" height="40"
fill="#00e4ff" style="filter:url(#cyan-glow)"/>
<rect transform="translate(120,0)" x="0" y="0" width="40" height="40"
fill="#00e4ff" style="filter:url(#cyan-glow)" mask="url(#Mask1)"/>
<path transform="translate(0,60)" d="M0,0 L40,0 L40,40 L0,40 z" stroke="#00e4ff"
stroke-width="3" fill="none"/>
<path transform="translate(60,60)" d="M0,0 L40,0 L40,40 L0,40 z" stroke="#00e4ff"
stroke-width="3" style="filter:url(#cyan-glow)" fill="none"/>
<path transform="translate(120,60)" d="M0,0 L40,0 L40,40 L0,40 z" stroke="#00e4ff"
stroke-width="3" style="filter:url(#cyan-glow)" fill="none" mask="url(#Mask2)"/>
</g>
</svg>

Blurring svg shapes as they pass through clip path

Example below:
I'd like to maintain a fixed area in the middle that has svg circles being animated through it.
Most examples I've seen involve some sort of merge that ends up showing both the original circle and the blurred version. Led me to something like this:
<clipPath id="top-mask">
<rect id="top-mask-rect" x="0" y="-100" width="100" height="100" />
</clipPath>
<filter id="top-blur" x="-200%" y="-200%" width="500%" height="500%">
<feimage xlinkHref="url(#top-mask)" result="mask" />
<feGaussianBlur stdDeviation="1.5" result="blur" />
<feComposite in2="mask" in="blur" operator="in" result="comp" />
<feMerge result="merge">
<feMergeNode in="SourceGraphic" />
<feMergeNode in="comp" />
</feMerge>
</filter>
This seems to overlay the circles on top of a slightly blurred version. My next naive step was to remove the feComposite and instead apply clipPath to both feMergeNode which didn't work at all.
What is the correct approach to this problem?
Things are not that elementary. You have to stack things a bit on top of one another for this to work. You need a mask to show the un-blurred object outside, and a filter to show it inside the designated area. Both need units defined as userSpaceOnUse.
#moving {
animation: move 5s ease-in-out infinite;
}
#keyframes move {
0% { transform: translate(0px, 0px) }
50% { transform: translate(300px, 0px) }
100% { transform: translate(0px, 0px) }
}
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"
height="300" width="500">
<defs>
<mask id="mask"
maskUnits="userSpaceOnUse">
<rect width="100%" height="100%" fill="white" />
<rect id="still" x="150" y="0" width="200" height="300" fill="black" />
</mask>
<filter id="blur" x="150" y="0" width="200" height="300"
filterUnits="userSpaceOnUse">
<feGaussianBlur stdDeviation="10" />
</filter>
</defs>
<g mask="url(#mask)">
<circle id="moving" r="60" cy="150" cx="100" fill="blue" />
</g>
<g filter="url(#blur)">
<use xlink:href="#moving" />
</g>
</svg>
The way the filter works is interesting. It seems the object is clipped before the blur is applied, resulting in a blur near the border of the designated area instead of a hard cut. I am unsure whether to call this a bug or expected behavior. (A quick test showed this both for Firefox and Chrome.)

How to graph a political coordinates graph

I'm trying to program a graph like this one:
I tried with SVG, but it's not very good since I had to use 2 different rectangles and didn't manage to get only the 4 edges to be rounded.
Here's my code:
<svg width="400" height="250">
<defs>
<linearGradient id="solids" x1="0%" y1="0%" x2="100%" y2="0%">
<stop offset="0%" style="stop-color:rgb(255,0,0);stop-opacity:1" />
<stop offset="50%" style="stop-color:rgb(255,0,0);stop-opacity:1" />
<stop offset="50%" style="stop-color:rgb(0,255,0);stop-opacity:1" />
<stop offset="100%" style="stop-color:rgb(0,255,0);stop-opacity:1" />
</linearGradient>
<linearGradient id="solids2" x1="0%" y1="0%" x2="100%" y2="0%">
<stop offset="0%" style="stop-color:rgb(255,255,0);stop-opacity:1" />
<stop offset="50%" style="stop-color:rgb(255,255,0);stop-opacity:1" />
<stop offset="50%" style="stop-color:rgb(0,0,255);stop-opacity:1" />
<stop offset="100%" style="stop-color:rgb(0,0,255);stop-opacity:1" />
</linearGradient>
</defs>
<text x="135" y="12" style="fill:black;">Conservadorismo
<tspan x="150" y="240">Liberalismo</tspan>
<tspan x="20" y="125">Esquerda</tspan>
<tspan x="305" y="125">Direita</tspan>
</text>
<rect x="100" y="20" rx="20" ry="20" width="200" height="100" style="fill:url(#solids); opacity:0.76" />
<rect x="100" y="120" rx="20" ry="20" width="200" height="100" style="fill:url(#solids2); opacity:0.76" />
<line x1="100" y1="120" x2="300" y2="120" style="stroke:black;stroke-width:2" />
<line x1="200" y1="20" x2="200" y2="220" style="stroke:black;stroke-width:2" />
</svg>
What should I do to fix it or do it better?
I would use ordinary <rect> objects without rounded corners, and apply a clipPath to the whole drawing to round off the corners.
Here's a simple example:
<svg width="400" height="400" viewBox="0 0 400 400">
<defs>
<clipPath id="roundRect">
<rect x="10" y="10" rx="20" ry="20" width="380" height="380"/>
</clipPath>
</defs>
<g clip-path="url(#roundRect)">
<rect fill="#0a0" stroke="none" x="10" y="10" width="190" height="190"/>
<rect fill="#f00" stroke="none" x="200" y="10" width="190" height="190"/>
<rect fill="#0bf" stroke="none" x="10" y="200" width="190" height="190"/>
<rect fill="#fd0" stroke="none" x="200" y="200" width="190" height="190"/>
</g>
</svg>

Alpha Transparent Gradient in Inline SVG Defs Element

I have this CODEPEN and here are my issues:
I am not understanding why the gradient I applied and referenced as my mask fill like so, doesn't render as it should. It should go from fully opaque to fully transparent. For the gradient I am using: http://angrytools.com/gradient/?0_800080,100_450045&0_0,100_100&l_180:
<mask id="myMask" x="0" y="0" width="100%" height="100%">
<rect x="0" y="0" width="100%" height="100%" fill="url(#grad1)" />
</mask>
In addition I don't understand why if I remove the fill="blue" attribute from my use element like so:
<use xlink:href="#myText" mask="url(#myMask)" />
The text appears black as if no gradient was applied. The gradient I defined is purple..
Thanks!
if you just want to apply your gradient to your text, there is no need to use masks, because gradients support the stop-opacity property.
<svg xmlns="http://www.w3.org/2000/svg" width="200px" height="200px">
<defs>
<linearGradient id="lgrad" x1="100%" y1="50%" x2="0%" y2="50%">
<stop offset="0%" style="stop-color:rgb(128,0,128);stop-opacity:0" />
<stop offset="100%" style="stop-color:rgb(69,0,69);stop-opacity:1" />
</linearGradient>
<text x="100" y="120" text-anchor="middle" id="myText" font-size="50">Hello</text>
</defs>
<use xlink:href="#myText" fill="url(#lgrad)" />
</svg>
you only need masks if you want to seperate the opacity from your fills:
<svg xmlns="http://www.w3.org/2000/svg" width="200px" height="200px">
<defs>
<linearGradient id="lgrad" x1="100%" y1="50%" x2="0%" y2="50%">
<stop offset="0" stop-color="black" />
<stop offset="1" stop-color="white" />
</linearGradient>
<mask id="myMask" x="0" y="0" width="100%" height="100%">
<rect x="0" y="0" width="100%" height="100%" fill="url(#lgrad)" />
</mask>
<text x="100" y="120" text-anchor="middle" id="myText" font-size="50">Hello</text>
</defs>
<g mask="url(#myMask)">
<use xlink:href=" #myText" transform="translate(0,-50) " fill="red " />
<use xlink:href="#myText" transform="translate(0,0)" fill="green" />
<use xlink:href="#myText" transform="translate(0,50)" fill="blue" />
</g>
</svg>
masks turn colors into opacity information. going from black(totally transparent) to white (totally opaque)
<svg xmlns="http://www.w3.org/2000/svg" width="200px" height="200px">
<defs>
<mask id="myMask" x="0" y="0" width="100%" height="100%">
<rect x="0" y="0" width="50%" height="50%" fill="white" />
<rect x="50%" y="0" width="50%" height="50%" fill="#333" />
<rect x="0%" y="50%" width="50%" height="50%" fill="#aaa" />
<rect x="50%" y="50%" width="50%" height="50%" fill="white" />
<circle cx="50%" cy="50%" r="15%" fill="black" />
</mask>
<text x="100" y="120" text-anchor="middle" id="myText" font-size="50">Hello</text>
</defs>
<rect x="0" y="0" width="100%" height="100%" fill="beige" />
<g mask="url(#myMask)">
<use xlink:href="#myText" transform="translate(0,-50)" fill="red" />
<use xlink:href="#myText" transform="translate(0,0)" fill="green" />
<use xlink:href="#myText" transform="translate(0,50)" fill="blue" />
</g>
</svg>

svg: filtering background image, google chrome

I am struggling with an svg to blur background under text on Google Chrome 36.0.1985.125 linux. The svg is like
<svg width="500px" height="500px" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<defs>
<filter id="myfilter" filterUnits="objectBoundingBox" x="0" y="0" width="100%" height="100%">
<feGaussianBlur result="blurOut" in="BackgroundImage" stdDeviation="2" />
<feBlend in2="blurOut" in="SourceGraphic" mode="normal" />
</filter>
</defs>
<g enable-background="new">
<text x="10" y="100" stroke="none" fill="red" fill-opacity="1" font-size="24">BACKGROUND</text>
<text x="20" y="100" stroke="none" fill="black" fill-opacity="1" font-size="26" filter="url(#myfilter)">text</text>
</g>
</svg>
Fiddle: http://jsfiddle.net/2o2trpc1/
Thus I would like to blur "BACKGROUND" behind "text", but "text" does not appear at all. Can someone please look at this what I am doing wrong? Where can I check that the browser version supports filtering background image?
thanks a lot,
Balazs
You will have to work around the lack of BackgroundImage. There are multiple ways to do that, if your code is as simple as the fiddle you posted something like this could work:
<body>
<svg width="500px" height="500px" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<defs>
<filter id="blur" filterUnits="objectBoundingBox" x="0" y="0" width="100%" height="100%">
<feGaussianBlur result="blurOut" stdDeviation="2" />
</filter>
</defs>
<g>
<text x="10" y="100" stroke="none" fill="red" fill-opacity="1" font-size="24" filter="url(#blur)">BACKGROUND</text>
<text x="20" y="100" stroke="none" fill="black" fill-opacity="1" font-size="26">text</text>
</g>
</svg>
</body>
See fiddle.
Another option is to use <feImage xlink:href="#background"/> in the filter, instead of using BackgroundImage. This can bring in whatever element you want.
<body>
<svg width="500px" height="500px" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<defs>
<filter id="myfilter" filterUnits="userSpaceOnUse" x="0" y="0" width="100%" height="100%">
<feImage xlink:href="#background"/>
<feGaussianBlur stdDeviation="3" />
<feBlend in="SourceGraphic" mode="normal" />
</filter>
<text id="background" x="10" y="100" stroke="none" fill="red" fill-opacity="1" font-size="24">BACKGROUND</text>
</defs>
<g>
<text x="20" y="100" stroke="none" fill="black" fill-opacity="1" font-size="26" filter="url(#myfilter)">text</text>
</g>
</svg>
</body>
See fiddle.

Resources