I have an SVG with a gradient-like background and a foreground icon. Currently, the icon is just one filled path with polylines on top, but it can be instead split into multiple paths where the polyline would be the stroke. However, I want to, instead of the polyline/stroke having a colour, have them display the background, as though it has cut a hole in the foreground. Due to the background being a gradient (it's actually a shadow filter) and not a solid colour, I can't just have a stroke with the same colour as the background to do this. This is what I imagine the result would be if I could have a negative stroke-width on the multiple paths.
As an example, I'll use the SVG this is actually for (my profile picture) So, this is my current SVG code
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" stroke="black" fill="rgb(95,95,95)" viewBox="-30 -30 276 260" width="276" height="260" stroke-width="4">
<defs>
<filter id="shadow" x="-30" y="-30" width="276" height="260">
<feGaussianBlur in="SourceAlpha" out="blurOut" stdDeviation="32"/>
<feBlend in="SourceGraphic" in2="blurOut" mode="normal"/>
</filter>
</defs>
<rect x="-30" y="-30" width="276" height="260" fill="#c41313" stroke="none"/>
<path d="M108 0 L216 54 L216 103 L162 130 L162 152 L135 166 L135 187 L108 200 L82 187 L82 166 L54 152 L54 130 L0 103 L0 54 Z" filter="url(#shadow)"/>
<g fill="none">
<polyline points="82 166, 108 179, 135 166"/>
<polyline points="54 130, 108 157, 162 130"/>
<polyline points="0 54, 108 108, 216 54"/>
<polyline points="49 78.5, 108 49, 167 78.5"/>
<line x1="108" y1="0" x2="108" y2="49"/>
<line x1="108" y1="200" x2="108" y2="108"/>
</g>
</svg>
I would like, instead of the black outline, it to be a gap in the icon, like this.
Of course, for this particular image, I could just manually make each section- separated by the black outlines- have it's own path without a stroke, re-creating the effect, but due to how I am using this image, I need the stroke-width to be variable, and having specific paths would not allow for this, unless there were a way to automatically generate these paths in Javascript or PHP but I wouldn't know how to implement this. The only way I can think of getting around this would be to find the centres of each section and increase a grey outline to decrease the gap (so a maximum gap would be a stroke-width of 0 and a minimum gap would have a stroke-width equivalent to the radius of each section), but for my situation this would still not be effective. So what alternatives are there?
CodePudding user response:
You can do this in a single filter with a green-screen technique. You make the exterior and interior lines pure blue and green and then use a ColorMatrix to select them into separate layers and them composite/out them from the original shape. It's important to use shape-rendering=crispEdges because there are anti-aliasing artifacts that looks bad if you don't.
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" stroke="black" fill="rgb(95,95,95)" viewBox="-30 -30 276 260" width="276" height="260" stroke-width="4" color-interpolation-filters="sRGB" shape-rendering="crispEdges">
<defs>
<filter id="green-screen" x="-30" y="-30" width="276" height="260">
<feColorMatrix type="matrix" values="0 0 0 0 0 0 2 0 0 -1 0 0 0 0 0 -1 2 -1 0 -1" result="exterior-line"/>
<feColorMatrix in="SourceGraphic" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 2 0 -1 -1 -1 2 0 -1" result="interior-line"/>
<feComposite operator="out" in="SourceGraphic" in2="exterior-line"/>
<feComposite operator="out" in2="interior-line" result="masked-shape"/>
<feGaussianBlur in="SourceAlpha" out="blurOut" stdDeviation="32"/>
<feComposite operator="over" in="masked-shape"/>
</filter>
</defs>
<rect x="-30" y="-30" width="276" height="260" fill="#c41313" stroke="none"/>
<g filter="url(#green-screen)">
<path d="M108 0 L216 54 L216 103 L162 130 L162 152 L135 166 L135 187 L108 200 L82 187 L82 166 L54 152 L54 130 L0 103 L0 54 Z" stroke="rgb(0,255,0)"/>
<g fill="none" stroke="rgb(0,0,255)">
<polyline points="82 166, 108 179, 135 166"/>
<polyline points="54 130, 108 157, 162 130"/>
<polyline points="0 54, 108 108, 216 54"/>
<polyline points="49 78.5, 108 49, 167 78.5"/>
<line x1="108" y1="0" x2="108" y2="49"/>
<line x1="108" y1="200" x2="108" y2="108"/>
</g>
</g>
</svg>
CodePudding user response:
Using CSS mix-blend-mode seems to achieve what you want, not sure how to fix the gray and the shadow yet though:
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" stroke="black" fill="rgb(95,95,95)" viewBox="-30 -30 276 260" width="276" height="260" stroke-width="4">
<defs>
<filter id="shadow" x="-30" y="-30" width="276" height="260">
<feGaussianBlur in="SourceAlpha" out="blurOut" stdDeviation="32"/>
<feBlend in="SourceGraphic" in2="blurOut" mode="normal"/>
</filter>
</defs>
<rect x="-30" y="-30" width="276" height="260" fill="#c41313" stroke="none"/>
<g stroke="black" style="mix-blend-mode: color-dodge;">
<path d="M108 0 L216 54 L216 103 L162 130 L162 152 L135 166 L135 187 L108 200 L82 187 L82 166 L54 152 L54 130 L0 103 L0 54 Z" filter="url(#shadow)"/>
<g fill="none">
<polyline points="82 166, 108 179, 135 166"/>
<polyline points="54 130, 108 157, 162 130"/>
<polyline points="0 54, 108 108, 216 54"/>
<polyline points="49 78.5, 108 49, 167 78.5"/>
<line x1="108" y1="0" x2="108" y2="49"/>
<line x1="108" y1="200" x2="108" y2="108"/>
</g>
</g>
</svg>
CodePudding user response:
A more conventional approach would be to use a <mask>
:
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 276 260" width="276" height="260">
<defs>
<mask id="mask" stroke-width="4" stroke="#000">
<g transform="translate(30 30)">
<path id="shapeBG" d="M108 0 L216 54 L216 103 L162 130 L162 152 L135 166 L135 187 L108 200 L82 187 L82 166 L54 152 L54 130 L0 103 L0 54 Z" fill="#fff" />
<g fill="none">
<polyline points="82 166, 108 179, 135 166" />
<polyline points="54 130, 108 157, 162 130" />
<polyline points="0 54, 108 108, 216 54" />
<polyline points="49 78.5, 108 49, 167 78.5" />
<line x1="108" y1="0" x2="108" y2="49" />
<line x1="108" y1="200" x2="108" y2="108" />
</g>
</g>
</mask>
<filter id="shadow" x="0" y="0" width="276" height="260">
<feGaussianBlur in="SourceAlpha" out="blurOut" stdDeviation="32" />
<feBlend in="SourceGraphic" in2="blurOut" mode="normal" />
</filter>
</defs>
<rect id="bg" x="0" y="0" width="100%" height="100%" fill="#c41313" stroke="none" />
<g filter="url(#shadow)">
<rect x="0" y="0" width="100%" height="100%" mask="url(#mask)" fill="rgb(95,95,95)" />
</g>
</svg>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="-30 -30 276 260" width="276" height="260">
<g id="mask" stroke-width="4" stroke="#000">
<path id="shapeBG" d="M108 0 L216 54 L216 103 L162 130 L162 152 L135 166 L135 187 L108 200 L82 187 L82 166 L54 152 L54 130 L0 103 L0 54 Z" fill="#fff" />
<g fill="none">
<polyline points="82 166, 108 179, 135 166" />
<polyline points="54 130, 108 157, 162 130" />
<polyline points="0 54, 108 108, 216 54" />
<polyline points="49 78.5, 108 49, 167 78.5" />
<line x1="108" y1="0" x2="108" y2="49" />
<line x1="108" y1="200" x2="108" y2="108" />
</g>
<text style="font-size:11; font-family:Arial, sans-serif" stroke-width="0" x="0" y="80%" dominant-baseline="middle">Mask: white fills/strokes=opaque; <tspan x="0" dy="12">black fills/strokes=transparent<tspan></text>
</svg>
On the right you see the actual mask graphic:
- white fills/strokes will become opaque
- black fills/strokes will become transparent
<mask>
could also be used to get semi-transparent strokes e.g by setting your stroke color to something like rgb(128,128,128)
.
Regarding software support you might also realign your graphics via a transform to avoid negative viewBox values: some editors have issues with these.