Blurring svg shapes as they pass through clip path - svg

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.)

Related

How to include SVG within an SVG document which has JavaScript code?

Please understand this question properly before marking as duplicate.
There are many questions like this one in StackOverflow, but my question is slightly different from those.
I have a chakra.svg file, whose code is this:
<svg xmlns="http://www.w3.org/2000/svg" width="100" height="100" id="b">
<circle cx="50" cy="50" r="45" stroke-width="2" stroke="blue" fill="none"/>
<script>
<![CDATA[
for(i=0;i<24;i++)
{
var l=document.createElementNS("http://www.w3.org/2000/svg","line");
l.setAttributeNS(null,"x1","50");
l.setAttributeNS(null,"y1","50");
l.setAttributeNS(null,"x2","5");
l.setAttributeNS(null,"y2","50");
l.setAttributeNS(null,"stroke-width",2);
l.setAttributeNS(null,"stroke","blue");
l.setAttributeNS(null,"transform","rotate("+(i/24*360)+",50,50)");
document.querySelector("#b").appendChild(l);
}
]]>
</script>
</svg>
The output is rendered as expected, like this:
I have another file, India_flag.svg whose code is this:
<svg xmlns:xlink="http://www.w3.org/1999/xlink" xmlns="http://www.w3.org/2000/svg" width="170" height="100">
<rect x="0" y="0" height="33" width="180" fill="#f93"/>
<rect x="0" y="33" height="34" width="180" fill="white"/>
<rect x="0" y="67" height="33" width="180" fill="green"/>
</svg>
The output of this file is also rendered as expected:
But, now the problem is, when I am trying to insert the chakra.svg file inside the India_flag.svg file using the image tag like this:
<image xlink:href="chakra.svg" x="70" y="35" height="30" width="30"/>
The output should have been that the wheel is placed at the centre of the flag, but I am getting this output:
The chakra.svg file is rendered, but the JavaScript code in that file did not run, only the circle is rendered. What am I doing wrong and how to achieve my objective?
SVG documents when loaded inside an <image> tag (or HTML <img> for that matter) are sand-boxed and won't allow the execution of scripts, nor will they be interactive (i.e no user-event).
To circumvent that, you need an other way to append you svg. The simple solution would be to copy its markup directly in the main one:
<svg xmlns:xlink="http://www.w3.org/1999/xlink" xmlns="http://www.w3.org/2000/svg" width="170" height="100">
<rect x="0" y="0" height="33" width="180" fill="#f93"/>
<rect x="0" y="33" height="34" width="180" fill="white"/>
<rect x="0" y="67" height="33" width="180" fill="green"/>
<svg x="70" y="35" height="30" width="30" id="b" viewBox="0 0 100 100">
<circle cx="50" cy="50" r="45" stroke-width="2" stroke="blue" fill="none"/>
<script>
<![CDATA[
for(i=0;i<24;i++) {
var l=document.createElementNS("http://www.w3.org/2000/svg","line");
l.setAttribute("x1","50");
l.setAttribute("y1","50");
l.setAttribute("x2","5");
l.setAttribute("y2","50");
l.setAttribute("stroke-width",2);
l.setAttribute("stroke","blue");
l.setAttribute("transform","rotate("+(i/24*360)+",50,50)");
document.querySelector("#b").appendChild(l);
}
]]>
</script>
</svg>
</svg>
An other solution, would have been to do the inverse: load your flag image from the one with the script: plnkr.

How to make vertical svg rect height start from the bottom?

Desired behaviour
Make a "bar chart" style icon with vertical bars.
Actual Behaviour
The following displays the desired result if I flip it with scaleY().
/* uncomment below for desired appearance */
/*
svg {
transform: scaleY(-1);
}
*/
<svg style="background: yellow" width="20" height="20" viewbox="0 0 20 20">
<rect x="2" y="0" width="1" height="5" style="fill:rgb(0,0,0)"/>
<rect x="7" y="0" width="1" height="8" style="fill:rgb(0,0,0)"/>
<rect x="12" y="0" width="1" height="15" style="fill:rgb(0,0,0)"/>
<rect x="17" y="0" width="1" height="10" style="fill:rgb(0,0,0)"/>
Sorry, your browser does not support inline SVG.
</svg>
Question
What is the correct way to start the height from the bottom, rather than the top?
SVG's Y axis goes from the top down to the bottom (source)[https://developer.mozilla.org/en-US/docs/Web/SVG/Tutorial/Positions#The_grid].
In order to display the bars from the bottom of the icon, you need to calculate each bar's y coordinate accordingly: iconHeight - barHeight.
Adapted snippet:
/* uncomment below for desired appearance */
/*
svg {
transform: scaleY(-1);
}
*/
<svg style="background: yellow" width="20" height="20" viewbox="0 0 20 20">
<rect x="2" y="15" width="1" height="5" style="fill:rgb(0,0,0)"/>
<rect x="7" y="12" width="1" height="8" style="fill:rgb(0,0,0)"/>
<rect x="12" y="5" width="1" height="15" style="fill:rgb(0,0,0)"/>
<rect x="17" y="10" width="1" height="10" style="fill:rgb(0,0,0)"/>
Sorry, your browser does not support inline SVG.
</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.

Make Blend Modes in SVG actually work?

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".

Simple fill pattern in svg : diagonal hatching

How would I fill an SVG shape, not with a single colour, an image or a gradient, but with a hatching pattern, diagonal if possible.
It's been 2 hours and I've found nothing (at least after 2005).
I figure a possible hack would be a hatched PNG that would serve as fill, but that is not ideal.
I did not find anything for diagonal hatching on the internet either, so I'll share my solution here:
<pattern id="diagonalHatch" patternUnits="userSpaceOnUse" width="4" height="4">
<path d="M-1,1 l2,-2
M0,4 l4,-4
M3,5 l2,-2"
style="stroke:black; stroke-width:1" />
</pattern>
(note the lower case "l" in the path expression)
The above creates a hatch with diagonal lines from the lower left to the upper right that are 4 pixels apart. Besides the diagonal line (M0,4 l4,-4) you also have to stroke the upper left and the lower right edges of the pattern area, since the line will otherwise be "constricted" due to clipping where it intersects the edges of the square.
To fill a rectangle with this pattern, do:
<rect x="0" y="0" width="100%" height="100%" fill="url(#diagonalHatch)"/>
Use the patternTransform attribute to rotate a vertical (or horizontal) line segment. This method tiles seamlessly and uses the simplest possible path. The pattern width attribute controls how close parallel hatches are.
<pattern id="diagonalHatch" width="10" height="10" patternTransform="rotate(45 0 0)" patternUnits="userSpaceOnUse">
<line x1="0" y1="0" x2="0" y2="10" style="stroke:black; stroke-width:1" />
</pattern>
This code from http://bl.ocks.org/jfsiii/7772281 seems very clean and reusable:
svg {
width: 500px;
height: 500px;
}
rect.hbar {
mask: url(#mask-stripe)
}
.thing-1 {
fill: blue;
}
.thing-2 {
fill: green;
}
<!DOCTYPE html>
<html>
<head>
<meta charset=utf-8 />
<title>SVG colored patterns via mask</title>
</head>
<body>
<svg>
<defs>
<pattern id="pattern-stripe"
width="4" height="4"
patternUnits="userSpaceOnUse"
patternTransform="rotate(45)">
<rect width="2" height="4" transform="translate(0,0)" fill="white"></rect>
</pattern>
<mask id="mask-stripe">
<rect x="0" y="0" width="100%" height="100%" fill="url(#pattern-stripe)" />
</mask>
</defs>
<!-- bar chart -->
<rect class="hbar thing-2" x="0" y="0" width="50" height="100"></rect>
<rect class="hbar thing-2" x="51" y="50" width="50" height="50"></rect>
<rect class="hbar thing-2" x="102" y="25" width="50" height="75"></rect>
<!-- horizontal bar chart -->
<rect class="hbar thing-1" x="0" y="200" width="10" height="50"></rect>
<rect class="hbar thing-1" x="0" y="251" width="123" height="50"></rect>
<rect class="hbar thing-1" x="0" y="302" width="41" height="50"></rect>
</svg>
</body>
</html>
You may be able to create, what you want using a <pattern> tag.
As a starting point you might take this example of the respective MDN docu:
<?xml version="1.0"?>
<svg width="120" height="120" viewBox="0 0 120 120"
xmlns="http://www.w3.org/2000/svg" version="1.1"
xmlns:xlink="http://www.w3.org/1999/xlink">
<defs>
<pattern id="Triangle"
width="10" height="10"
patternUnits="userSpaceOnUse">
<polygon points="5,0 10,10 0,10"/>
</pattern>
</defs>
<circle cx="60" cy="60" r="50"
fill="url(#Triangle)"/>
</svg>
One problem with drawing a diagonal line within a pattern is that when the pattern is tiled the lines won't always line up - especially at high zooms. (It depends on the SVG rendering engine you happen to be using).
#Ingo's answer above attempts to resolve this by drawing in the triangles at the top-left and bottom-right corners - but again, using some rendering engines and high zooms, it doesn't always look best - and sometimes the line ends up looking a bit like a string of sausages.
Another approach is to draw a horizontal line in the pattern and rotate the pattern, e.g.
<svg:svg viewBox="0 0 100 100" version="1.1"
xmlns:svg="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink">
<svg:defs>
<svg:pattern id="diagonalHatch" patternUnits="userSpaceOnUse" width="4" height="4" patternTransform="rotate(45 2 2)">
<svg:path d="M -1,2 l 6,0" stroke="#000000" stroke-width="1"/>
</svg:pattern>
</svg:defs>
<svg:rect x="0" y="0" height="100" width="100" fill="url(#diagonalHatch)"/>
These two resources are very helpful:
https://bocoup.com/weblog/using-svg-patterns-as-fills
https://github.com/iros/patternfills/blob/master/public/patterns.css
For example:
<svg xmlns='http://www.w3.org/2000/svg' width='10' height='10'>
<rect width='10' height='10' fill='red'/>
<path d='M-1,1 l2,-2
M0,10 l10,-10
M9,11 l2,-2' stroke='orange' stroke-width='2'/>
</svg>
This is a solution for diagonal lines using circle in pattern. You can change angle as per your requirements.
<svg width="500" height="500">
<defs>
<pattern id="transformedPattern"
x="0" y="0" width="2" height="20"
patternUnits="userSpaceOnUse"
patternTransform="rotate(45)">
<circle cx="1" cy="1" r="2" style="stroke: none; fill: #0000ff" />
</pattern>
</defs>
<rect x="10" y="10" width="100" height="100"
style="stroke: #000000; fill: url(#transformedPattern);" />
</svg>
I tried with this sample. Hopefully, It can help you much.
<!DOCTYPE html>
<html>
<head>
<meta charset=utf-8 />
<title>SVG colored patterns via mask</title>
</head>
<body>
<svg viewBox="0 0 300 300" xmlns="http://www.w3.org/2000/svg">
<defs>
<pattern id="stripes" viewBox="0,0,8,8" width="16" height="16" patternUnits="userSpaceOnUse">
<polygon points="0,0 4,0 0,4" fill="yellow"></polygon>
<polygon points="0,8 8,0 8,4 4,8" fill="yellow"></polygon>
<polygon points="0,4 0,8 8,0 4,0" fill="green"></polygon>
<polygon points="4,8 8,8 8,4" fill="green"></polygon>
</pattern>
</defs>
<rect fill="url(#stripes)" x="150" y="20" width="100" height="50" />
<circle cx="50" cy="50" r="50" fill="url(#stripes)"/>
</svg>
</body>
</html>
Regards,
Vu Phan
SVG 2 has a hatch entity for specifically this purpose. From that page's example section:
<hatch hatchUnits="userSpaceOnUse" pitch="5" rotate="135">
<hatchpath stroke="#a080ff" stroke-width="2"/>
</hatch>
This is a very easily configurable way to create hatches:
Furthermore the hatch path can also be customised:
<hatchpath stroke-width="1" d="C 0,4 8,6 8,10 8,14 0,16 0,20"/>
For React Native use can use this component, for making background lines pattern.
You should add to your project react-native-svg
import PropTypes from 'prop-types';
import React, { PureComponent } from "react";
import { View } from "react-native";
import Svg, { Defs, Line, Pattern, Rect } from 'react-native-svg';
export default class PatternLineView extends PureComponent {
static propTypes = {
pattern: PropTypes.func.isRequired,
space: PropTypes.number,
backgroundColor: PropTypes.string,
lineColor: PropTypes.string,
lineWidth: PropTypes.number,
rotation: PropTypes.number
}
static defaultProps = {
pattern: () => { },
space: 8,
lineColor: "#D2D9E5",
lineWidth: 3,
rotation: 45
}
render() {
const transform = `rotate(${this.props.rotation})`
return <View style={{
flex: 1,
flexDirection: "row",
height: "100%",
width: "100%",
position: "absolute",
top: 0,
start: 0,
backgroundColor: this.props.backgroundColor
}}>
<Svg width="100%" height="100%">
<Defs>
<Pattern
id="linePattern"
patternUnits="userSpaceOnUse"
patternTransform={transform}
width={this.props.space}
height={this.props.space}>
<Line
x1="0"
y1="0"
x2="0"
y2="100%"
stroke={this.props.lineColor}
strokeWidth={this.props.lineWidth}
/>
</Pattern>
</Defs>
<Rect
fill="url(#linePattern)"
x="0"
y="0"
width="100%"
height="100%"
/>
</Svg>
</View>
}
}
I've adapted Ingo's answer here.
<defs>
<pattern id="diagonalHatch" patternUnits="userSpaceOnUse" width="4" height="4">
<!-- background -->
<path id="background"
d="M-1,3 L3,-1
M1,5 L5,1" style="stroke:pink; stroke-width:10" />
<!-- hatches -->
<path id="hatches"
d="M-2,2 L2,-2
M0,4 L4,0
M2,6 L6,2" style="stroke:red; stroke-width:1" />
</pattern>
</defs>
This pattern includes two paths, one for the background, and other for the hatches. The background color is addressable vs JS such as:
const hatchPath = document.querySelector("path#hatches");
hatchPath.setAttribute('style', "stroke:blue; stroke-width:1")
The background path is overly-wide on purpose, so that there's no part of the pattern not at least covered by the background. Meanwhile, the hatches can have their width tuned to change how thick the lines are.
Some great points got lost in the comments, so I wanted to aggregate that knowledge with some simpler inline examples. As far as I can tell, what method you choose to use is entirely up to preference since the heavy lifting is being done by patternTransform + rotate either way, but personally I think the <rect> method is easier to digest at-a-glance even if it might make more semantic sense to use <line>.
SVG Non-Scaling Pattern with <rect>
Define the spacing between your lines with the <pattern>'s width, and the width of the lines themselves via the nested <rect>'s width.
Codepen example as full-size background.
<svg height="100%" width="100%" xmlns="http://www.w3.org/2000/svg">
<pattern id="diaHatch" width="9" height="1" patternUnits="userSpaceOnUse" patternTransform="rotate(45)">
<rect x="0" y="0" width="3" height="1" fill="red" />
</pattern>
<rect x="0" y="0" width="100%" height="100%" fill="url(#diaHatch)" />
</svg>
SVG Non-Scaling Pattern with <line>
Define the spacing between the <line>s with the pattern height, and use stroke-width for the <line> itself.
Codepen example as full-size background.
<svg height="100%" width="100%" xmlns="http://www.w3.org/2000/svg">
<pattern id="diaHatch" width="1" height="9" patternUnits="userSpaceOnUse" patternTransform="rotate(45)">
<line x1="0" x2="100%" y1="0" y2="0" stroke-width="9" stroke="black" />
</pattern>
<rect x="0" y="0" width="100%" height="100%" fill="url(#diaHatch)" />
</svg>
On SVG 2 & Hatches [as of October 2022]
While the above examples solve the issue, SVG 2's Hatches (Candidate Recommendation 2016) are a documented method of tackling this exact issue. Inkscape happens to implement them because one of its developers, Tavmjong Bah, was an Invited Expert at the time, but browsers are still tackling them. To note, the latest Editor's Draft (2018) doesn't include hatch, which doesn't necessarily mean anything, but might be why it hasn't yet been prioritized by browser vendors.
Tracking SVG 2 Features for the Browser
Each major engine has an ongoing thread for feature implementations:
SVG 2 in Gecko
SVG 2 in Blink
SVG 2 in Webkit

Resources