Can the opacity / translucence of an SVG group be adjusted together? - svg

I have an SVG "g" object that has several components. I'd like to render the whole thing partly transparent (e.g. alpha = 0.5) I'd also like to to be darker if possible. I know individual fill colors can be adjusted, but how about all of them together, possibly in some parameters to the "g" (grouping) structure.

Changing the opacity of the <g> (either by the opacity="..." attribute, or the opacity CSS rule) will cause the contents of the group to first be composited and then the result to be lowered in opacity. Note that this is distinctly different from lowering the opacity on all the elements inside the group (which you can also do via CSS):
Demo: http://jsfiddle.net/HZr7v/12/
Uses this SVG:
<svg xmlns="http://www.w3.org/2000/svg" width="400" height="400">
<defs><filter id="Darker">
<feColorMatrix type="matrix" values="
0.3 0 0 0 0
0 0.3 0 0 0
0 0 0.3 0 0
0 0 0 0.8 0"/>
</filter></defs>
<g id="g1" transform="translate(60,60)"> <!-- top left -->
<circle r="40" /><rect width="80" height="60" />
</g>
<g id="g2" transform="translate(220,60)"><!-- top right -->
<circle r="40" /><rect width="80" height="60" />
</g>
<g id="g3" transform="translate(60,200)"><!-- bottom left -->
<circle r="40" /><rect width="80" height="60" />
</g>
<g id="g4" transform="translate(220,200)"><!-- bottom right -->
<circle r="40" /><rect width="80" height="60" />
</g>
</svg>
…with this CSS:
circle { fill:red }
rect { fill:blue }
#g2 * { opacity:0.5 } /* Change the opacity of each shape */
#g3 { opacity:0.5 } /* Change the opacity of the group */
#g4 { filter:url(#Darker) } /* Darkens and drops opacity for group */
Note in particular the difference in appearance where the circle and square overlap.
The feColorMatrix filter looks daunting. All it does is set the RGB values to each be 30% of the original RGB (i.e. darker), and the alpha to be 80% of the original alpha. Change the values as you see fit.
Oh, and if you want to desaturate also you can throw this into the filter (before or after the darken step):
<feColorMatrix type="saturate" values="0.5"/>
<!-- values="0" will drop all color, leaving grayscale only -->
<!-- values="1" will leave the current saturation color -->
<!-- values="99" will super-saturate the colors -->

You could set opacity on the <g> itself and that would work. If you want it darker you'll need to apply a filter to the <g> something along these lines perhaps
<filter id="Darker" filterUnits="objectBoundingBox" x="0" y="0" width="100%" height="100%">
<feFlood in="SourceGraphic" flood-color="#0f0" flood-opacity="0.5" result="img2"/>
<feBlend in="SourceGraphic" in2="img2" mode="darken"/>
</filter>

Related

Drawing a partial circle, with varying height, without gradients (for leaflet map markers)

I'm trying to generate an svg icon to use as Leaflet map markers. There's a circle which should be partially filled (vertically) based on a percentage variable between 0 and 1 (0 = no fill, 1 = full circle). I managed to accomplish what I need using a linear gradient, and that was working fine in Google Maps. Now I'm migrating to Leaflet, and apparently Leaflet doesn't support linear gradients in icons, as it doesn't render it properly. Or maybe it just doesn't support referencing elements with url().
This is what I have been using, using 0.4 as the fill percentage:
<svg width="25px" height="25px" viewBox="0 0 100 100" version="1.1">
<linearGradient id="perc" x1="0.4" y1="1" x2="0.4" y2="0">
<stop stop-color="#80C000" offset="0.4"/>
<stop stop-color="#fff" />
</linearGradient>
<ellipse id="outsideCircle" fill="#80C000" cx="50" cy="50" rx="42.8" ry="42.8"/>
<ellipse id="middleCircle" fill="url(#perc)" cx="50" cy="50" rx="41.2" ry="41.2"/>
<ellipse id="insideCircle" stroke="#80C000" fill="#EFFADC" stroke-width="1.6" cx="50" cy="50" rx="32" ry="32"/>
<text text-anchor="middle" font-family="Verdana" font-size="320%" font-weight="bold" dominant-baseline="central" x="50" y="48" fill="#80C000">A</text>
</svg>
Which renders as (enlarged for clarity):
However, using the exact same code in Leaflet always renders the full green circle, and I'm not sure why. So I'm looking for another way to accomplish this, maybe using Arcs? It would be great if I could generate the icons based on the same percentage variable, but I'm also open to just using 11 static icons (0, 0.1, 0.2, ..., 0.9, 1) and selecting one to use based on the percentage, rounding it.
I've found this question with some promising results, but I've been unable to adapt it to my use case.
An alternative solution would be using a line path that you clip with the circle. Now you can use the stroke-dasharray attribute to set the stroke and the gap length of the path.
I'm using an input type range only to show how the result for different values.
let len = line.getTotalLength();
itr.addEventListener("input",()=>{
let dash = itr.value * len/100;
line.setAttribute("stroke-dasharray",`${dash},${100 - dash}`);
console.clear();console.log(itr.value)
})
<P><input type="range" value="25" id="itr"/></p>
<svg width="100" viewBox="0 0 100 100" version="1.1">
<text text-anchor="middle" font-family="Verdana" font-size="320%" font-weight="bold" dominant-baseline="central" x="50" y="48" fill="#80C000">A</text>
<clipPath id="clip">
<path id="circ" d="M92.8,50A42.8, 42.8 0 1 1 7.2,50A42.8,42.8 0 1 1 92.8,50 M82,50A32,32 0 1 0 18,50 A32,32 0 1 0 82,50" />
</clipPath>
<path id="line" stroke="#80C000" stroke-width="86" d="M50,92.8V7.2" clip-path="url(#clip)" stroke-dasharray="25 75" />
<use href="#circ" fill="none" stroke="#80C000" />
</svg>
Observation: I'm rewriting thecircle as a path with a hole in the middle like so:
<svg width="100" viewBox="0 0 100 100" version="1.1">
<path id="circ" d="M92.8,50A42.8, 42.8 0 1 1 7.2,50A42.8,42.8 0 1 1 92.8,50 M82,50A32,32 0 1 0 18,50 A32,32 0 1 0 82,50" />
</svg>
In order to draw a hole into the circle I'm drawing first the outer circle clockwise and nextnthe inner circle counterclockwise. I will use this shape for the clipPath but also for a <use> element to draw the green stroke of the circle.

Adding feDropShadow to a vertical line in SVG makes it disappear

I have the following SVG document:
<svg preserveAspectRatio="xMinYMin meet" viewBox="0 0 21 484" xmlns="http://www.w3.org/2000/svg">
<defs>
<filter id="dropShadow">
<feDropShadow dx="4" dy="0" stdDeviation="4"></feDropShadow>
</filter>
</defs>
<g id="Artboard" stroke-width="5" stroke="#FF0000" fill="#000000" stroke-linecap="round">
<path style="filter: url(#dropShadow)" d="M7.5,8.5 L7.5,471.5" id="path-1"></path>
</g>
</svg>
In Firefox, when I open the SVG document, it simply shows a very thin (not 5 wide) vertical line. In Chrome, it doesn't show anything (nor does it in codepen, here: https://codepen.io/jwir3/pen/BJBqEK ).
I'm not quite sure what I'm doing incorrectly here, but it has something to do with the filter, because, if I remove the filter: url(#dropShadow) from the path definition, the line shows up as expected.
You can't use objectBoundingBox units if your shape has no height or width.
Keyword objectBoundingBox should not be used when the geometry of the applicable element has no width or no height, such as the case of a horizontal or vertical line, even when the line has actual thickness when viewed due to having a non-zero stroke width since stroke width is ignored for bounding box calculations. When the geometry of the applicable element has no width or height and objectBoundingBox is specified, then the given effect (e.g., a gradient or a filter) will be ignored.
The default for filterUnits is objectBoundingBox units so you need to change that to userSpaceOnUse i.e.
<svg preserveAspectRatio="xMinYMin meet" viewBox="0 0 21 484" xmlns="http://www.w3.org/2000/svg">
<title>Line Drop Shadow</title>
<description>A red line with 5px width thickness and round caps, having a drop-shadow. This highlights the regression documented in PURP-1017.</description>
<defs>
<filter id="dropShadow" filterUnits="userSpaceOnUse">
<feDropShadow dx="4" dy="0" stdDeviation="4"></feDropShadow>
</filter>
</defs>
<g id="Artboard" stroke-width="5" stroke="#FF0000" fill="#000000" stroke-linecap="round">
<path style="filter: url(#dropShadow)" d="M7.5,8.5 L7.5,471.5" id="path-1"></path>
</g>
</svg>
When processing filters, different browsers process in different stroke.
Chrome considers stroke as a value with a zero pixel, so it does not include it in the filter region.
Therefore, to make the result look the same in different browsers, it is better to replace path with stroke-width ="5", a rectangle with a width of 5px withoutstroke (stroke="none")
In addition, the default values for the filter area are: x =" - 10% "" y = "- 10%" `` width = "120%" `` height = "120%"- large blur sizes are usually truncated .
By default, filterUnits = "objectBoundingBox" and therefore the values are specified in percentages.
To make it easier to calculate the size of the filter region action, specify the value offilterUnits = "userSpaceOnUse" and then you can specify all dimensions for thefilter region` in pixels.
<svg preserveAspectRatio="xMinYMin meet" width="100%" height="100%" viewBox="0 0 21 484" xmlns="http://www.w3.org/2000/svg" >
<defs>
<filter id="dropShadow" filterUnits = "userSpaceOnUse" x="4" y="0" width="12" height="472">
<feDropShadow dx="6" dy="4" stdDeviation="3"></feDropShadow>
</filter>
</defs>
<g id="Artboard" fill="#FF0000" filter="url(#dropShadow)" >
<!-- <path style="filter: url(#dropShadow)" d="M7.5,8.5 L7.5,471.5" id="path-1" stroke-width="5" ></path>-->
<rect x="5" y="5" width="5" stroke="none" height="463" />
</g>
</svg>
Swapping to userSpaceOnUse is the correct answer in most circumstances but has the following limitations:
The filter effects region will apply from -10% to 120% of the canvas, rather than the bounding box of the element (using more memory and processing time)
For large dynamic SVGs (such as created by d3) it can be hard to calculate the required filter x/y/width/height to ensure the filter applies to all elements.
An alternate (less elegant) solution is to apply the filter to a <g> and use a hidden node within this to give the group the correct width or height:
<svg xmlns="http://www.w3.org/2000/svg">
<defs>
<filter id="dropShadow" width="20">
<feDropShadow dx="4" dy="0" stdDeviation="4"></feDropShadow>
</filter>
</defs>
<g id="Artboard" style="filter: url(#dropShadow)">
<circle r="5" cx="0" cy="0" visibility="hidden"></circle>
<path d="M10,10 L10,100" stroke-width="5" stroke="#FF0000" fill="#000000" stroke-linecap="round"></path>
</g>
</svg>

Using SVG filter to create an inside stroke

I would like to simulate and "inside stroke" on an svg path.
I have an svg map with multiple complex paths (countries) each with a different fill color stroke.
And i would like to add a "fake inside stroke" in the first one.
I managed to get a few things done with the inner-shadow trick (with gaussian blur filter) but can't manage to have it as "non-blurry".
The ideal solution would be as an svg filter so i can apply it dynamicaly via JS without changing path or manipulating dom.
Thanks a lot !
Edit 1 :
So far i tried this trick but the fake shadow is sometimes over the stroke and always blurry so i'm not sure that's even the best way ...
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="150" height="150" style="transform:scale(2);transform-origin:0 0 ">
<defs>
<filter id='inset' x='-50%' y='-50%' width='200%' height='200%'>
<feFlood fill-color="black"/>
<feComposite in2="SourceAlpha" operator="out"/>
<feGaussianBlur stdDeviation='10' edgeMode="none" />
<feOffset dx='0' dy='0' result='offsetblur'/>
<feFlood flood-color='#00ff00' result='color'/>
<feComposite in2='offsetblur' operator='in'/>
<feComposite in2='SourceAlpha' operator='in' />
<feMerge>
<feMergeNode in='SourceGraphic'/>
<feMergeNode/>
</feMerge>
</filter>
</defs>
<path class="st0" d="M144.7,126.2l-2.8,8.8l-3.9-2.3l-2-7.7l1.7-4.3l5.5-4.4L144.7,126.2z M93.5,24.2l6,6.3l4.4-1l7.5,6l1.9,1.1
l2.5-0.3l4,3.4l12.3,2.4l-4.3,8.9l-1.1,9.1l-2.4,2.2l-3.9-1.2l0.3,3.2l-6.3,7l-0.1,5.6l4.1-1.9l2.9,5.4L121,84l2.5,4.6l-3,3.7
l2.2,9.3l4.6,1.5l-1,5.1l-7.8,6.6l-16.9-3.2l-12.5,3.8l-1,7l-9.9,1.5l-9.6-5.3l-3.1,2.5l-15.8-5.3l-3.4-4.6l4.4-7.1l1.6-24.1
l-8.8-13l-6.3-6.4l-13.1-4.9l-0.9-9.4l11.1-2.8L48.9,47l-2.7-14.8l8.1,5.7l20-10.3l2.6-11l7.5-2.8l1.3,4.8l4,0.2L93.5,24.2z" stroke-width="1" fill="#00ffff" stroke="#FF0000" filter="url(#inset)"/>
</svg>
If you want clear shape, you should use SVG transform instead of applying CSS transform to svg element.
And when you draw "inside stroke", the feMorphorogy element is useful. This reduces(or increases) paint area of target shape, thus you can draw "fake inside/outside" stroke.
<svg xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink" width="300" height="300">
<defs>
<filter id='inset' x='-50%' y='-50%' width='200%' height='200%'>
<!--outside-stroke-->
<feFlood flood-color="red" result="outside-color"/>
<feMorphology in="SourceAlpha" operator="dilate" radius="2"/>
<feComposite in="outside-color" operator="in" result="outside-stroke"/>
<!--inside-stroke-->
<feFlood flood-color="blue" result="inside-color"/>
<feComposite in2="SourceAlpha" operator="in" result="inside-stroke"/>
<!--fill-area-->
<feMorphology in="SourceAlpha" operator="erode" radius="2"/>
<feComposite in="SourceGraphic" operator="in" result="fill-area"/>
<!--merge graphics-->
<feMerge>
<feMergeNode in="outside-stroke"/>
<feMergeNode in="inside-stroke"/>
<feMergeNode in="fill-area"/>
</feMerge>
</filter>
</defs>
<g transform="scale(2)">
<path class="st0" d="M144.7,126.2l-2.8,8.8l-3.9-2.3l-2-7.7l1.7-4.3l5.5-4.4L144.7,126.2z M93.5,24.2l6,6.3l4.4-1l7.5,6l1.9,1.1
l2.5-0.3l4,3.4l12.3,2.4l-4.3,8.9l-1.1,9.1l-2.4,2.2l-3.9-1.2l0.3,3.2l-6.3,7l-0.1,5.6l4.1-1.9l2.9,5.4L121,84l2.5,4.6l-3,3.7
l2.2,9.3l4.6,1.5l-1,5.1l-7.8,6.6l-16.9-3.2l-12.5,3.8l-1,7l-9.9,1.5l-9.6-5.3l-3.1,2.5l-15.8-5.3l-3.4-4.6l4.4-7.1l1.6-24.1
l-8.8-13l-6.3-6.4l-13.1-4.9l-0.9-9.4l11.1-2.8L48.9,47l-2.7-14.8l8.1,5.7l20-10.3l2.6-11l7.5-2.8l1.3,4.8l4,0.2L93.5,24.2z"
fill="#00ffff" filter="url(#inset)"/>
</g>
</svg>
This does what you want. Please note that it depends on the stroke being a single color that's distinct from any of the fill colors (in this case 100% red - you can change the stroke color to anything you want but the filter gets more complicated).
You can adjust the color of the "fake" inner stroke by altering the values in the last column of the final feColorMatrix. Right now, it's 100% blue. (You can also use feMorphology to create this - as in def's answer - but that approach does not preserve the original mitering.)
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="150" height="150" style="transform:scale(2);transform-origin:0 0 ">
<defs>
<filter id='fake-stroke' x='-50%' y='-50%' width='200%' height='200%' color-interpolation-filters="sRGB">
<!-- select just the red outline and zero out the opacity of everything that's not 100% red. -->
<feColorMatrix type="matrix" values="1 0 0 0 0
0 0 0 0 0
0 0 0 0 0
255 -255 -255 -254 0" result="outline-only"/>
<feGaussianBlur stdDeviation="1"/>
<!-- select just the blur - not the original stroke. -->
<feComposite operator="out" in2="outline-only"/>
<!-- select just the blur that overlaps the original content -->
<feComposite operator="in" in2="SourceGraphic" />
<!-- increase its opacity to 100% except the most blurred - to fake anti-aliasing -->
<feComponentTransfer>
<feFuncA type="table" tableValues="0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1"/>
</feComponentTransfer>
<!-- change the color of the fake stroke to the desired value -->
<feColorMatrix type="matrix" values ="0 0 0 0 0
0 0 0 0 0
0 0 0 0 1
0 0 0 1 0"/>
<!-- put it on top of the original -->
<feComposite operator="over" in2="SourceGraphic"/>
</filter>
</defs>
<path class="st0" d="M144.7,126.2l-2.8,8.8l-3.9-2.3l-2-7.7l1.7-4.3l5.5-4.4L144.7,126.2z M93.5,24.2l6,6.3l4.4-1l7.5,6l1.9,1.1
l2.5-0.3l4,3.4l12.3,2.4l-4.3,8.9l-1.1,9.1l-2.4,2.2l-3.9-1.2l0.3,3.2l-6.3,7l-0.1,5.6l4.1-1.9l2.9,5.4L121,84l2.5,4.6l-3,3.7
l2.2,9.3l4.6,1.5l-1,5.1l-7.8,6.6l-16.9-3.2l-12.5,3.8l-1,7l-9.9,1.5l-9.6-5.3l-3.1,2.5l-15.8-5.3l-3.4-4.6l4.4-7.1l1.6-24.1
l-8.8-13l-6.3-6.4l-13.1-4.9l-0.9-9.4l11.1-2.8L48.9,47l-2.7-14.8l8.1,5.7l20-10.3l2.6-11l7.5-2.8l1.3,4.8l4,0.2L93.5,24.2z" stroke-width="2" fill="#00ffff" stroke="#FF0000" filter="url(#fake-stroke)"/>
</svg>
I tried both of the existing answers and I found that they altered the shape of the inner contour in a messy way. My solution below covers the requirements except that the solution would ideally use an SVG filter. I used the clip-path feature instead as it allowed me to preserve the correct miters and also not result in a blurry inset.
defghi1977's solution results in odd outline contours that do not match the original shape.
Michael Mullany's solution results in a good outer contour but the inner one is heavily blurred.
My solution results in sharp contours and correct miters.
SVG markup
<svg version="1.1" xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
width="600" height="300" viewbox="0 0 300 150">
<defs>
<clipPath id="inset1">
<path id="area1" d="M144.7,126.2l-2.8,8.8l-3.9-2.3l-2-7.7l1.7-4.3l5.5-4.4L144.7,
126.2z M93.5,24.2l6,6.3l4.4-1l7.5,6l1.9,1.1l2.5-0.3l4,3.4l
12.3,2.4l-4.3,8.9l-1.1,9.1l-2.4,2.2l-3.9-1.2l0.3,3.2l-6.3,7l
-0.1,5.6l4.1-1.9l2.9,5.4L121,84l2.5,4.6l-3,3.7l2.2,9.3l4.6,
1.5l-1,5.1l-7.8,6.6l-16.9-3.2l-12.5,3.8l-1,7l-9.9,1.5l-9.6
-5.3l-3.1,2.5l-15.8-5.3l-3.4-4.6l4.4-7.1l1.6-24.1l-8.8-13l
-6.3-6.4l-13.1-4.9l-0.9-9.4l11.1-2.8L48.9,47l-2.7-14.8l8.1,
5.7l20-10.3l2.6-11l7.5-2.8l1.3,4.8l4,0.2L93.5,24.2z"/>
</clipPath>
</defs>
<use href="#area1" stroke-width="6" fill="cyan" stroke="blue" clip-path="url(#inset1)"/>
<use href="#area1" stroke-width="2" fill="none" stroke="red"/>
</svg>
Iterating from Don's idea here you might need to further separate each line and even add a fill with no overlaps -- perhaps because you need some transparency or texture.
If moving the shape into defs is possible in given use-case and interactivity is not strictly necessary, it is possible to further derive disjunct masks from it that each cover distinct area: "outer border" (beach), "inner border" (cliffs), and even "fill" (inland):
:root {
background: dimgray;
color: snow;
}
svg {
--a: darkslategray;
--b: teal;
background-image: repeating-conic-gradient(var(--a) 0 25%, var(--b) 0 50%);
background-size: 1em 1em;
}
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="600" height="300" viewbox="0 0 300 150">
<use mask="url(#beach)" fill="darkblue" opacity=".6" href="#canvas" />
<use mask="url(#cliffs)" fill="red" opacity=".4" href="#canvas" />
<use mask="url(#inland)" fill="lime" opacity=".3" href="#canvas" />
<defs>
<!-- Both "stroke halves" in single stroke-width: -->
<g id="coast">
<use href="#shoreline" stroke-width="10" />
</g>
<!-- Area with the outer half of the stroke: -->
<mask id="beach">
<use href="#coast" stroke="white" />
<use href="#shoreline" fill="black" />
</mask>
<!-- For cutting the outer half of the stroke: -->
<clipPath id="sea">
<use href="#shoreline" />
</clipPath>
<!-- Area with the inner half of the stroke: -->
<mask id="cliffs">
<use href="#coast" stroke="white" clip-path="url(#sea)" />
</mask>
<!-- Area inside inner stroke: -->
<mask id="inland">
<use href="#coast" stroke="black" fill="white" />
</mask>
<!-- Viewport cover: -->
<rect id="canvas" width="100%" height="100%" />
<!-- The shape: -->
<path id="shoreline" d="M144.7,126.2l-2.8,8.8l-3.9-2.3l-2-7.7l1.7-4.3l5.5-4.4L144.7,
126.2z M93.5,24.2l6,6.3l4.4-1l7.5,6l1.9,1.1l2.5-0.3l4,3.4l
12.3,2.4l-4.3,8.9l-1.1,9.1l-2.4,2.2l-3.9-1.2l0.3,3.2l-6.3,7l
-0.1,5.6l4.1-1.9l2.9,5.4L121,84l2.5,4.6l-3,3.7l2.2,9.3l4.6,
1.5l-1,5.1l-7.8,6.6l-16.9-3.2l-12.5,3.8l-1,7l-9.9,1.5l-9.6
-5.3l-3.1,2.5l-15.8-5.3l-3.4-4.6l4.4-7.1l1.6-24.1l-8.8-13l
-6.3-6.4l-13.1-4.9l-0.9-9.4l11.1-2.8L48.9,47l-2.7-14.8l8.1,
5.7l20-10.3l2.6-11l7.5-2.8l1.3,4.8l4,0.2L93.5,24.2z"
/>
</defs>
</svg>
(Credits:) See nice Drawing inner/outer strokes in SVG (clips and masks) article by Alex Chan demonstrating this technique.

SVG: getting rid of shape outlines using feBlend multiply blending

I have an SVG where I'm using filters to blend white shapes on top of black backgrounds using multiply blending. Since I'm using multiply, I would expect the white shape not to show up at all, but this is not the case. In this example I'm drawing a square in its own filter, then a smaller circle (also in its own filter):
http://jsfiddle.net/mugwhump/SwcjL/2/
However, there is a faint, pixel-wide white outline surrounding the circle. The color and opacity of this outline depends on the color and opacity used to draw the circle.
It disappears if I move the circle into the same filter group as the square, but unfortunately that's not an option.
Any idea how to get rid of that outline? This happens in basically all browsers/renderers.
Here's the code for the svg:
<svg contentScriptType="text/ecmascript" width="530"
xmlns:xlink="http://www.w3.org/1999/xlink" zoomAndPan="magnify"
contentStyleType="text/css" viewBox="0 0 530 530" height="530"
preserveAspectRatio="xMidYMid meet" xmlns="http://www.w3.org/2000/svg"
version="1.1">
<defs id="defs">
<!--Blend using multiply. -->
<filter color-interpolation-filters="sRGB" x="-1.0" y="-1.0" width="3.0"
xmlns:xlink="http://www.w3.org/1999/xlink" xlink:type="simple"
xlink:actuate="onLoad" height="3.0"
xlink:show="other" id="blend-square-multiply">
<feFlood result="blackness" flood-color="black"/>
<feComposite result="blackness clip" in="blackness" in2="SourceGraphic" operator="in"/>
<feColorMatrix in="SourceGraphic" values="1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 1 0" type="matrix" result="color-trans"/>
<feBlend mode="multiply" in="blackness clip" in2="color-trans"/>
</filter>
</defs>
<!-- White square blended with multiply over black feFlood -->
<g filter="url(#blend-square-multiply)">
<g fill="white" >
<rect x="200" width="100" y="200" height="100" />
</g>
</g>
<!-- White circle blended with multiply over black feFlood
Changing stroke width does nothing, but changing fill color changes
the color of the outline (changing stroke-color does not).
The outline disappears when opacity is set to 0. -->
<g filter="url(#blend-square-multiply)">
<g fill="white" opacity="1">
<circle stroke-width="0" cx="250" cy="250" r="50"/>
</g>
</g>
Remember that source graphics are rasterized before they're handed to the filter pipeline and hence shed their vectorish nature when they enter the threshold of filterdom.
You are getting that effect because the circle element has an edge of anti-aliasing aka semi-opaque pixels around its edges. When those are multiplied with black, you get grey.
Use shape-rendering="crispEdges" to avoid this. Or use an feMorphology erode filter to clip the edges of the antialiasing. Or use an feFuncA filter to dial those edges up to full opacity first. On second thought -- that first idea.
<circle shape-rendering="crispEdges" stroke-width="0" cx="250" cy="250" r="50"/>
I think the enable-background attribute may be the answer to your problem.
http://www.w3.org/TR/SVG/filters.html#EnableBackgroundProperty

Outline a group of touching shapes with SVG

For a certain style I'm going for, I want to outline a group of shapes in SVG. Applied to a group, the stroke property appears to outline each shape individually--effectively going on top of other shapes nearby. To explain my situation more clearly: I have a group of touching rectangles that are 8x8 pixels each. They do not form a larger rectangle, however.
For simplicity's sake, let's say they form a cross. So I have 5 rectangles--1 in the center and one more on each side of that one. I want to outline them all as if they were 1 shape. Given that this "cross" shape changes, I would prefer not to use paths since that would require a lot more coding. Isn't there any way that I could get the effects filter to recognize this group as a single shape?
If not, is it at least possible to make a black copy of this group that's exactly 2px larger in width and height that I can position behind the group to create a solid black outline? And if so, is it possible without duplicating the group?
Thank you for any help.
You could use an svg filter like this one for example:
<filter id="outline">
<feMorphology operator="dilate" in="SourceAlpha" radius="1"/>
<feComposite in="SourceGraphic"/>
</filter>
Use the filter like this:
<g filter="url(#outline)">
<circle fill="lime" cx="20" cy="10" r="5"/>
<rect x="40" y="10" width="100" height="10" fill="lime"/>
<line x1="20" y1="10" x2="80" y2="15" stroke="lime"/>
</g>
Another alternative that might work, depending on how your content looks is something like this:
<use xlink:href="#g" stroke-width="10" stroke="black"/>
<g id="g">
<circle fill="lime" cx="20" cy="10" r="5"/>
<rect x="40" y="10" width="100" height="10" fill="lime"/>
<circle fill="lime" cx="140" cy="10" r="5"/>
<circle fill="lime" cx="120" cy="10" r="5"/>
</g>
Like this:
<svg xmlns="http://www.w3.org/2000/svg">
<defs>
<filter id="biggerbwcopy">
<feColorMatrix values="0 0 0 0 0
0 0 0 0 0
0 0 0 0 0
0 0 0 1 0"/>
<feMorphology operator="dilate" radius="2"/>
</filter>
</defs>
<rect id="r" x="10" y="10" width="20" height="20" fill="blue" onclick="biggerbw()"/>
<script>
function biggerbw() {
document.getElementById("r").style.filter="url(#biggerbwcopy)";
}
</script>
</svg>
http://jsfiddle.net/longsonr/LrDHT/1/ click on the rectangle and it becomes black and bigger.
You could extend the filter to put the original shape on top using feMerge

Resources