how to unite path and change shape in svg - javascript

I drew two arcs that together can be a circle.
I could not unite them.
1 - How to combine them?
path-1 example path-1
example path-2
path1= d="M75,250 C 100,50 250,50 275,250"
path2= d="M 275,250 C 275,450 75,450 75,250"
I want to replace the triangle in a circle.
2 - how to do it?
tringle shape: d="M-25,-12.5 L25,-12.5 L 0,-87.5 z" is inside the path.
Thanks..
my code:
<svg xmlns="http://www.w3.org/2000/svg" version="1.1">
<line class="line_svg" x1="75" y1="100" x2="275" y2="100" />
<line class="line_svg" x1="75" y1="250" x2="275" y2="250" />
<line class="line_svg" x1="75" y1="100" x2="75" y2="400" />
<line class="line_svg" x1="175" y1="100" x2="175" y2="400" />
<line class="line_svg" x1="275" y1="100" x2="275" y2="400" />
<line class="line_svg" x1="75" y1="400" x2="275" y2="400" />
<path id="path1"
d="M 275,250 C 275,450 75,450 75,250" fill="none" stroke="blue" stroke-width="7.06" />
<circle cx="75" cy="250" r="18" fill="blue" />
<circle cx="175" cy="100" r="18" fill="blue" />
<circle cx="275" cy="250" r="18" fill="blue" />
<circle cx="175" cy="400" r="18" fill="blue" />
<path d="M-25,-12.5 L25,-12.5 L 0,-87.5 z" fill="yellow" stroke="red" stroke-width="7.06" >
<animateTransform
attributeName="transform"
begin="0s"
dur="10s"
type="rotate"
from="0 -100 -110"
to="360 150 150"
repeatCount="1" />
<animateMotion dur="11s" repeatCount="1" rotate="auto" >
<mpath xlink:href="#path1"/>
</animateMotion>
<circle id="pointA" cx="75" cy="250" r="5" />
</svg>​

Related

how to set svg path shadow only on outside?

Is it possible to get a shadow effect only on the outside border?
image
Directly from the docs: https://developer.mozilla.org/en-US/docs/Web/SVG/Element/feDropShadow#SVG
<svg viewBox="0 0 30 10" xmlns="http://www.w3.org/2000/svg">
<defs>
<filter id="shadow">
<feDropShadow dx="0.2" dy="0.4" stdDeviation="0.2"/>
</filter>
<filter id="shadow2">
<feDropShadow dx="0" dy="0" stdDeviation="0.5"
flood-color="cyan"/>
</filter>
<filter id="shadow3">
<feDropShadow dx="-0.8" dy="-0.8" stdDeviation="0"
flood-color="pink" flood-opacity="0.5"/>
</filter>
</defs>
<circle cx="5" cy="50%" r="4"
style="fill:pink; filter:url(#shadow);"/>
<circle cx="15" cy="50%" r="4"
style="fill:pink; filter:url(#shadow2);"/>
<circle cx="25" cy="50%" r="4"
style="fill:pink; filter:url(#shadow3);"/>
</svg>
Output (middle one should be what you need? Just need to change to a path - I will try to do that now):
With Path:
<svg viewBox="0 0 30 10" xmlns="http://www.w3.org/2000/svg">
<defs>
<filter id="shadow">
<feDropShadow dx="0.2" dy="0.4" stdDeviation="0.2"/>
</filter>
<filter id="shadow2">
<feDropShadow dx="0" dy="0" stdDeviation="0.5"
flood-color="cyan"/>
</filter>
<filter id="shadow3">
<feDropShadow dx="-0.8" dy="-0.8" stdDeviation="0"
flood-color="pink" flood-opacity="0.5"/>
</filter>
</defs>
<circle cx="5" cy="50%" r="4"
style="fill:pink; filter:url(#shadow);"/>
<path d="M11 1, v8, h8, v-8, z"
style="fill:white; filter:url(#shadow2);"/>
<circle cx="25" cy="50%" r="4"
style="fill:pink; filter:url(#shadow3);"/>
Output:
Now I see some issues, as it's not transparent:
<svg viewBox="0 0 30 10" xmlns="http://www.w3.org/2000/svg">
<defs>
<filter id="shadow">
<feDropShadow dx="0.2" dy="0.4" stdDeviation="0.2"/>
</filter>
<filter id="shadow2">
<feDropShadow dx="0" dy="0" stdDeviation="0.5"
flood-color="cyan" flood-opacity="0.5"/>
</filter>
<filter id="shadow3">
<feDropShadow dx="-0.8" dy="-0.8" stdDeviation="0"
flood-color="pink" flood-opacity="0.7"/>
</filter>
</defs>
<circle cx="5" cy="50%" r="4"
style="fill:pink; filter:url(#shadow);"/>
<circle cx="25" cy="50%" r="4"
style="fill:pink; filter:url(#shadow3);"/>
<mask id="myMask">
<!-- Everything under a white pixel will be visible -->
<rect x="0" y="0" width="100%" height="100%" fill="white" />
<!-- Everything under a black pixel will be invisible -->
<polygon fill="black" points="0.02,0.02 0.98,0.02 0.98,0.98 0.02,0.98 0.02,0.02" />
<!--
<path d="M14 1, v8, h8, v-8, z" fill="black" />
-->
</mask>
<rect x="16" y="1" width="8" height="8"
mask="url(#myMask)"
fill="white"
stroke="black"
stroke-width="0.2"
style="filter:url(#shadow2);"/>
<!--
<path d="M14 1, v8, h8, v-8, z"
style="fill:white; stroke:black; stroke-width: 0.2;
mask=url(#myMask);
filter:url(#shadow2);"/>
-->
<!--
<path d="M14 1, v8, h8, v-8, z"
style="fill:none; stroke:black; stroke-width: 0.2;
clip-path: polygon(0% 0%, 0% 100%, 1% 100%, 1% 1%, 99% 1%, 99% 99%, 1% 99%, 1% 100%, 100% 100%, 100% 0%);
filter:url(#shadow2);"/>
<rect x="14" y="1" width="8" height="8"
style="fill:white; stroke:black; stroke-width: 0.2;
clip-path: polygon(0% 0%, 0% 100%, 1% 100%, 1% 1%, 99% 1%, 99% 99%, 1% 99%, 1% 100%, 100% 100%, 100% 0%);
filter:url(#shadow2);"/>
-->
Output (not yet transparent):
Next try:
I can do it with a rect or a path (the circle is behind the rect/path and you see through) but I don't (yet) know how to do it for an arbitrary path:
<svg viewBox="0 0 200 100" xmlns="http://www.w3.org/2000/svg">
<defs>
<filter id="shadow2">
<feDropShadow dx="0" dy="0" stdDeviation="2"
flood-color="cyan" flood-opacity="0.7"/>
</filter>
</defs>
<mask id="myMask" maskContentUnits="objectBoundingBox">
<rect fill="white" x="-10%" y="-10%" width="120%" height="120%" />
<polygon fill="black" points="0.02,0.02 0.98,0.02 0.98,0.98 0.02,0.98 0.02,0.02" />
</mask>
<!--
Punch a hole in a shape of a square inside the white rect,
revealing the yellow circle underneath
-->
<circle cx="50" cy="50" r="20" fill="yellow" />
<!--
<rect x="25" y="25" height="50" width="50" fill="white"
stroke="black"
stroke-width="2"
mask="url(#myMask)"
style="filter:url(#shadow2);"
/>
-->
<path d="M25,25 v50, h50, v-50, z" fill="white"
stroke="black"
stroke-width="2"
mask="url(#myMask)"
style="filter:url(#shadow2);"
/>
</svg>
Output:
Another example:
<svg viewBox="0 0 30 10" xmlns="http://www.w3.org/2000/svg">
<defs>
<filter id="shadow">
<feDropShadow dx="0.2" dy="0.4" stdDeviation="0.2" />
</filter>
<filter id="shadow2">
<feDropShadow dx="0" dy="0" stdDeviation="0.8" flood-color="cyan" flood-opacity="0.7" />
</filter>
<filter id="shadow3">
<feDropShadow dx="-0.8" dy="-0.8" stdDeviation="0" flood-color="pink" flood-opacity="0.7" />
</filter>
</defs>
<mask id="myMask" maskContentUnits="objectBoundingBox">
<rect fill="white" x="-10%" y="-10%" width="120%" height="120%" />
<polygon fill="black" points="0.015,0.015 0.985,0.015 0.985,0.985 0.015,0.985 0.015,0.015" />
</mask>
<!--
Punch a hole in a shape of a square inside the white rect,
revealing the yellow circle underneath
-->
<circle cx="5" cy="50%" r="4" style="fill:pink; filter:url(#shadow);" />
<circle cx="25" cy="50%" r="4" style="fill:pink; filter:url(#shadow3);" />
<path d="M14 1, v8, h8, v-8, z" fill="white" stroke="black" stroke-width="0.2" mask="url(#myMask)" style="filter:url(#shadow2);" />
<!-- <rect x="14" y="1" height="8" width="8" fill="white" stroke="black" stroke-width="0.2" mask="url(#myMask)" style="filter:url(#shadow2);" /> -->
<!-- <circle cx="50" cy="50" r="20" fill="yellow" /> -->
<!-- <rect x="25" y="25" height="50" width="50" fill="white" stroke="black" stroke-width="2" mask="url(#myMask)" style="filter:url(#shadow2);" /> -->
</svg>
Output:

moving child nodes to another <g> element mess up the design

I have a working static svg graph with root node and 4 child nodes. I wanted to group the child nodes separately but that way the design gets broken up. Here is the working code
<svg id="svg" width="800" height="600" viewbox="0 0 800 600" xmlns="http://www.w3.org/2000/svg">
<g id="root_node" transform="matrix(1 0 0 1 250 100)">
<rect width="200" height="60" rx="5" ry="5">
</rect>
<text x="100" y="30" font-size="14" text-anchor="middle" fill="white">
Root Node
</text>
<line x1="0" y1="0" x2="100" y2="0" transform="translate(0, 0) rotate(-135)" stroke="black" stroke-width="3"/>
<line x1="0" y1="0" x2="100" y2="0" transform="translate(200 0) rotate(-45)" stroke="black" stroke-width="3"/>
<line x1="0" y1="0" x2="100" y2="0" transform="translate(0 60) rotate(135)" stroke="black" stroke-width="3"/>
<line x1="0" y1="0" x2="100" y2="0" transform="translate(200 60) rotate(45)" stroke="black" stroke-width="3"/>
</g>
<g id="child_node">
<rect width="180" height="60" x="0" y="0" rx="5" ry="5">
</rect>
<rect width="180" height="60" x="500" y="0" rx="5" ry="5">
</rect>
<rect width="180" height="60" x="0" y="200" rx="5" ry="5">
</rect>
<rect width="180" height="60" x="500" y="200" rx="5" ry="5">
</rect>
<text x="100" y="30" font-size="14" fill="white" text-anchor="middle">Child Node 1</text>
<text x="600" y="30" font-size="14" fill="white" text-anchor="middle">Child Node 2</text>
<text x="100" y="230" font-size="14" fill="white" text-anchor="middle">Child Node 3</text>
<text x="600" y="230" font-size="14" fill="white" text-anchor="middle">Child Node 4</text>
</g>
<g id="edges">
</g>
</svg>
when i changed the above code to following where I wanted to group the child node in one place, i dont get the above design
<svg id="svg" width="800" height="600" viewbox="0 0 800 600" xmlns="http://www.w3.org/2000/svg">
<g id="root_node" transform="matrix(1 0 0 1 250 100)">
<rect width="200" height="60" rx="5" ry="5">
</rect>
<text x="100" y="30" font-size="14" text-anchor="middle" fill="white">
Root Node
</text>
</g>
<g id="child_node">
<line x1="0" y1="0" x2="100" y2="0" transform="translate(0, 0) rotate(-135)" stroke="black" stroke-width="3"/>
<line x1="0" y1="0" x2="100" y2="0" transform="translate(200 0) rotate(-45)" stroke="black" stroke-width="3"/>
<line x1="0" y1="0" x2="100" y2="0" transform="translate(0 60) rotate(135)" stroke="black" stroke-width="3"/>
<line x1="0" y1="0" x2="100" y2="0" transform="translate(200 60) rotate(45)" stroke="black" stroke-width="3"/>
<rect width="180" height="60" x="0" y="0" rx="5" ry="5">
</rect>
<rect width="180" height="60" x="500" y="0" rx="5" ry="5">
</rect>
<rect width="180" height="60" x="0" y="200" rx="5" ry="5">
</rect>
<rect width="180" height="60" x="500" y="200" rx="5" ry="5">
</rect>
<text x="100" y="30" font-size="14" fill="white" text-anchor="middle">Child Node 1</text>
<text x="600" y="30" font-size="14" fill="white" text-anchor="middle">Child Node 2</text>
<text x="100" y="230" font-size="14" fill="white" text-anchor="middle">Child Node 3</text>
<text x="600" y="230" font-size="14" fill="white" text-anchor="middle">Child Node 4</text>
</g>
</svg>
I want to know the mechanism behind this. Can anyone explain me, please? Do i have to use transformation in child_nodes group to make it work?
Moving child notes to other group in your example messes up output because you are using matrix transformation on one group and not on other - original output it therefore result of child nodes' attributes and transformation on parent node (g).
If you exported SVG from somewhere (fe. Illustrator) it will be easier if you group nodes as required in original file. Or get rid of transform and set child notes accordingly.
I guess it's easier to open SVG in illustrator, do required changes and then export it again, as I did to get the attached snippet.
Other options is to combine groups matrix transformation and line's transformation. Basically your #root_node is just translated (x: 250px, y: 100px). In your case you would just needed to translate line x += 250 & y += 100.
<svg id="svg" width="800" height="600" viewbox="0 0 800 600" xmlns="http://www.w3.org/2000/svg">
<g id="root_node" transform="matrix(1 0 0 1 250 100)">
<rect width="200" height="60" rx="5" ry="5">
</rect>
<text x="100" y="30" font-size="14" text-anchor="middle" fill="white">
Root Node
</text>
</g>
<g id="child_node">
<rect width="180" height="60" x="0" y="0" rx="5" ry="5">
</rect>
<rect width="180" height="60" x="500" y="0" rx="5" ry="5">
</rect>
<rect width="180" height="60" x="0" y="200" rx="5" ry="5">
</rect>
<rect width="180" height="60" x="500" y="200" rx="5" ry="5">
</rect>
<text x="100" y="30" font-size="14" fill="white" text-anchor="middle">Child Node 1</text>
<text x="600" y="30" font-size="14" fill="white" text-anchor="middle">Child Node 2</text>
<text x="100" y="230" font-size="14" fill="white" text-anchor="middle">Child Node 3</text>
<text x="600" y="230" font-size="14" fill="white" text-anchor="middle">Child Node 4</text>
<!-- option 1 -->
<!-- use transform in lines (x1, y1) and (x2, y2) -->
<line class="st3" x1="250" y1="100" x2="179.3" y2="29.3" stroke="black" stroke-width="3"/>
<line class="st3" x1="450" y1="100" x2="520.7" y2="29.3" stroke="black" stroke-width="3"/>
<line class="st3" x1="250" y1="160" x2="179.3" y2="230.7" stroke="black" stroke-width="3"/>
<line class="st3" x1="450" y1="160" x2="520.7" y2="230.7" stroke="black" stroke-width="3"/>
<!-- option 2 -->
<!-- combine group transform (translate) and line's transform -->
<line x1="0" y1="0" x2="100" y2="0" transform="translate(250, 100) rotate(-135)" stroke="black" stroke-width="3"/>
<line x1="0" y1="0" x2="100" y2="0" transform="translate(450, 100) rotate(-45)" stroke="black" stroke-width="3"/>
<line x1="0" y1="0" x2="100" y2="0" transform="translate(250, 160) rotate(135)" stroke="black" stroke-width="3"/>
<line x1="0" y1="0" x2="100" y2="0" transform="translate(450, 160) rotate(45)" stroke="black" stroke-width="3"/>
</g>
<g id="edges">
</g>
</svg>
<svg id="svg" width="800" height="600" viewbox="0 0 800 600" xmlns="http://www.w3.org/2000/svg">
<g id="root_node" transform="matrix(1 0 0 1 250 100)">
<rect width="200" height="60" rx="5" ry="5">
</rect>
<text x="100" y="30" font-size="14" text-anchor="middle" fill="white">
Root Node
</text>
</g>
<g id="child_node">
<line x1="0" y1="0" x2="100" y2="0" transform="translate(250, 100) rotate(-135)" stroke="black" stroke-width="3"/>
<line x1="0" y1="0" x2="100" y2="0" transform="translate(450, 100) rotate(-45)" stroke="black" stroke-width="3"/>
<line x1="0" y1="0" x2="100" y2="0" transform="translate(250, 160) rotate(135)" stroke="black" stroke-width="3"/>
<line x1="0" y1="0" x2="100" y2="0" transform="translate(450, 160) rotate(45)" stroke="black" stroke-width="3"/>
<rect width="180" height="60" x="0" y="0" rx="5" ry="5">
</rect>
<rect width="180" height="60" x="500" y="0" rx="5" ry="5">
</rect>
<rect width="180" height="60" x="0" y="200" rx="5" ry="5">
</rect>
<rect width="180" height="60" x="500" y="200" rx="5" ry="5">
</rect>
<text x="100" y="30" font-size="14" fill="white" text-anchor="middle">Child Node 1</text>
<text x="600" y="30" font-size="14" fill="white" text-anchor="middle">Child Node 2</text>
<text x="100" y="230" font-size="14" fill="white" text-anchor="middle">Child Node 3</text>
<text x="600" y="230" font-size="14" fill="white" text-anchor="middle">Child Node 4</text>
</g>
</svg>

Animated SVG progress bar with steps

I have progress bar with steps like in this example:
http://jsbin.com/yuyojepuda/edit?html,css,js,output , but I need to animate it to fill the line from 0 to 100%.
.completed {
stroke: #FBAA33;
fill: #FBAA33;
}
.not-complete {
stroke: #555e6c;
fill: #555e6c;
}
<svg width="1000" height="1000" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<defs>
<g id="step">
<line x1="10" y1="50" x2="90" y2="50" style="stroke:#555e6c; stroke-width:10; stroke-linecap:round"/>
<circle cx="90" cy="50" r="20" style="fill:#555e6c; stroke-width:0;" />
<line x1="10" y1="50" x2="90" y2="50" style="stroke:inherit; stroke-width:5; stroke-linecap:round" z-index="100"/>
<circle cx="90" cy="50" r="17" style="fill:inherit; stroke-width:0;" />
</g>
</defs>
<use transform="translate(320 0)" class="not-complete" xlink:href="#step" />
<use transform="translate(240 0)" class="completed" xlink:href="#step" />
<use transform="translate(160 0)" class="completed" xlink:href="#step" />
<use transform="translate(80 0)" class="completed" xlink:href="#step" />
<use transform="translate(0 0)" class="completed" xlink:href="#step" />
<line x1="10" y1="50" x2="400" y2="50" style="stroke:#FBAA33; stroke-width:5; stroke-linecap:round" z-index="100"/>
</svg>
I found a lot of examples, like this: http://jsbin.com/yuyojepuda/2/edit?html,css,js,output , but I can't apply it on my code.
One more question. Is it possible instead these circles to put some DIVs or something else where I can put some (background-)image?

Calculate SVG.Text Length before drawing [duplicate]

I want to color the background of svg text similar to background-color in css
I was only able to find documentation on fill, which colors the text itself
Is it even possible?
You could use a filter to generate the background.
<svg width="100%" height="100%">
<defs>
<filter x="0" y="0" width="1" height="1" id="solid">
<feFlood flood-color="yellow" result="bg" />
<feMerge>
<feMergeNode in="bg"/>
<feMergeNode in="SourceGraphic"/>
</feMerge>
</filter>
</defs>
<text filter="url(#solid)" x="20" y="50" font-size="50">solid background</text>
</svg>
No this is not possible, SVG elements do not have background-... presentation attributes.
To simulate this effect you could draw a rectangle behind the text attribute with fill="green" or something similar (filters). Using JavaScript you could do the following:
var ctx = document.getElementById("the-svg"),
textElm = ctx.getElementById("the-text"),
SVGRect = textElm.getBBox();
var rect = document.createElementNS("http://www.w3.org/2000/svg", "rect");
rect.setAttribute("x", SVGRect.x);
rect.setAttribute("y", SVGRect.y);
rect.setAttribute("width", SVGRect.width);
rect.setAttribute("height", SVGRect.height);
rect.setAttribute("fill", "yellow");
ctx.insertBefore(rect, textElm);
The solution I have used is:
<svg>
<line x1="100" y1="100" x2="500" y2="100" style="stroke:black; stroke-width: 2"/>
<text x="150" y="105" style="stroke:white; stroke-width:0.6em">Hello World!</text>
<text x="150" y="105" style="fill:black">Hello World!</text>
</svg>
A duplicate text item is being placed, with stroke and stroke-width attributes. The stroke should match the background colour, and the stroke-width should be just big enough to create a "splodge" on which to write the actual text.
A bit of a hack and there are potential issues, but works for me!
Instead of using a <text> tag, the <foreignObject> tag can be used, which allows for XHTML content with CSS.
No, you can not add background color to SVG elements. You can do it programmatically with d3.
var text = d3.select("text");
var bbox = text.node().getBBox();
var padding = 2;
var rect = self.svg.insert("rect", "text")
.attr("x", bbox.x - padding)
.attr("y", bbox.y - padding)
.attr("width", bbox.width + (padding*2))
.attr("height", bbox.height + (padding*2))
.style("fill", "red");
Answer by Robert Longson (#RobertLongson) with modifications:
<svg width="100%" height="100%">
<defs>
<filter x="0" y="0" width="1" height="1" id="solid">
<feFlood flood-color="yellow"/>
<feComposite in="SourceGraphic" operator="xor"/>
</filter>
</defs>
<text filter="url(#solid)" x="20" y="50" font-size="50"> solid background </text>
<text x="20" y="50" font-size="50">solid background</text>
</svg>
and we have no bluring and no heavy "getBBox" :)
Padding is provided by white spaces in text-element with filter.
It's worked for me
Going further with #dbarton_uk answer, to avoid duplicating text you can use paint-order=stroke style:
<svg>
<line x1="100" y1="100" x2="350" y2="100" style="stroke:grey; stroke-width: 100"/>
<text x="150" y="105" style="stroke:white; stroke-width:0.5em; fill:black; paint-order:stroke; stroke-linejoin:round">Hello World!</text>
</svg>
Note the stroke-linejoin:round which is needed to avoid seeing spikes for the W sharp angle.
You can combine filter with the text.
<!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>
<filter x="0" y="0" width="1" height="1" id="bg-text">
<feFlood flood-color="white"/>
<feComposite in="SourceGraphic" operator="xor" />
</filter>
</defs>
<!-- something has already existed -->
<rect fill="red" x="150" y="20" width="100" height="50" />
<circle cx="50" cy="50" r="50" fill="blue"/>
<!-- Text render here -->
<text filter="url(#bg-text)" fill="black" x="20" y="50" font-size="30">text with color</text>
<text fill="black" x="20" y="50" font-size="30">text with color</text>
</svg>
</body>
</html>
For those wondering how to apply padding to a text element when it has a background like in the Robert's answer, do the following:
<svg>
<defs>
<filter x="-0.1" y="-0.1" width="1.2" height="1.2" id="solid">
<feFlood flood-color="#171717"/>
<feComposite in="SourceGraphic" operator="xor" />
</filter>
</defs>
<text filter="url(#solid)" x="20" y="50" font-size="50">Hello</text>
</svg>
In the example above, filter's x and y positions can be used as transform: translate(-10%, -10%) would, and width and height values can be read as 120% and 120%. So we made background 20% bigger, and offsetted it -10%, so background is now 10% bigger on each side of the text.
this is my favorite hack (not sure it should work). It refer an element that is not yet displayed, and it works pretty well
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 620 40" preserveAspectRatio="xMidYMid meet">
<defs>
<filter x="-0.02" y="0" width="1.04" height="1.1" id="removebackground">
<feFlood flood-color="#00ffff"/>
</filter>
</defs>
<!--Draw the text-->
<use xlink:href="#mygroup" filter="url(#removebackground)" />
<g id="mygroup">
<text id="text1" x="9" y="20" style="text-anchor:start;font-size:14px;">custom text with background</text>
<line x1="200" y1="18" x2="200" y2="36" stroke="#000" stroke-width="5"/>
<line x1="120" y1="27" x2="203" y2="27" stroke="#000" stroke-width="5"/>
</g>
</svg>
The previous answers relied on doubling up text and lacked sufficient whitespace.
By using atop and I was able to get the results I wanted.
This example also includes arrows, a common use case for SVG text labels:
<svg viewBox="-105 -40 210 234">
<title>Size Guide</title>
<defs>
<filter x="0" y="0" width="1" height="1" id="solid">
<feFlood flood-color="white"></feFlood>
<feComposite in="SourceGraphic" operator="atop"></feComposite>
</filter>
<marker id="arrow" viewBox="0 0 10 10" refX="5" refY="5" markerWidth="6" markerHeight="6" orient="auto-start-reverse">
<path d="M 0 0 L 10 5 L 0 10 z"></path>
</marker>
</defs>
<g id="garment">
<path id="right-body" fill="none" stroke="black" stroke-width="1" stroke-linejoin="round" d="M0 0 l30 0 l0 154 l-30 0"></path>
<path id="right-sleeve" d="M30 0 l35 0 l0 120 l-35 0" fill="none" stroke-linejoin="round" stroke="black" stroke-width="1"></path>
<use id="left-body" href="#right-body" transform="scale(-1,1)"></use>
<use id="left-sleeve" href="#right-sleeve" transform="scale(-1,1)"></use>
<path id="collar-right-top" fill="none" stroke="black" stroke-width="1" stroke-linejoin="round" d="M0 -6.5 l11.75 0 l6.5 6.5"></path>
<use id="collar-left-top" href="#collar-right-top" transform="scale(-1,1)"></use>
<path id="collar-left" fill="white" stroke="black" stroke-width="1" stroke-linejoin="round" d="M-11.75 -6.5 l-6.5 6.5 l30 77 l6.5 -6.5 Z"></path>
<path id="front-right" fill="white" stroke="black" stroke-width="1" d="M18.25 0 L30 0 l0 154 l-41.75 0 l0 -77 Z"></path>
<line x1="0" y1="0" x2="0" y2="154" stroke="black" stroke-width="1" stroke-dasharray="1 3"></line>
<use id="collar-right" href="#collar-left" transform="scale(-1,1)"></use>
</g>
<g id="dimension-labels">
<g id="dimension-sleeve-length">
<line marker-start="url(#arrow)" marker-end="url(#arrow)" x1="85" y1="0" x2="85" y2="120" stroke="black" stroke-width="1"></line>
<text font-size="10" filter="url(#solid)" fill="black" x="85" y="60" class="dimension" text-anchor="middle" dominant-baseline="middle"> 120 cm</text>
</g>
<g id="dimension-length">
<line marker-start="url(#arrow)" marker-end="url(#arrow)" x1="-85" y1="0" x2="-85" y2="154" stroke="black" stroke-width="1"></line>
<text font-size="10" filter="url(#solid)" fill="black" x="-85" y="77" text-anchor="middle" dominant-baseline="middle" class="dimension"> 154 cm</text>
</g>
<g id="dimension-sleeve-to-sleeve">
<line marker-start="url(#arrow)" marker-end="url(#arrow)" x1="-65" y1="-20" x2="65" y2="-20" stroke="black" stroke-width="1"></line>
<text font-size="10" filter="url(#solid)" fill="black" x="0" y="-20" text-anchor="middle" dominant-baseline="middle" class="dimension"> 130 cm </text>
</g>
<g title="Back Width" id="dimension-back-width">
<line marker-start="url(#arrow)" marker-end="url(#arrow)" x1="-30" y1="174" x2="30" y2="174" stroke="black" stroke-width="1"></line>
<text font-size="10" filter="url(#solid)" fill="black" x="0" y="174" text-anchor="middle" dominant-baseline="middle" class="dimension"> 60 cm </text>
</g>
</g>
</svg>
An obvious workaround to the problem of the blur produced by the filter effect is to render the <text> two times: once for the background (with transparent characters) and once for the characters (without a background filter).
For me, this was the only way to make the text readable in Safari.
<svg width="100%" height="100%">
<filter x="0" y="0" width="1" height="1" id="solid">
<feFlood flood-color="yellow" />
</filter>
<g transform="translate(20, 50)" font-size="50">
<text aria-hidden="true" fill="none" filter="url(#solid)">solid background</text>
<text fill="blue">solid background</text>
</g>
</svg>
The aria-hidden="true" attribute is there to prevent screen readers from speaking the text twice, if the user uses a screen reader.
You can add style to your text:
style="-webkit-tap-highlight-color: rgba(0, 0, 0, 0);
text-shadow: rgb(255, 255, 255) -2px -2px 0px, rgb(255, 255, 255) -2px 2px 0px,
rgb(255, 255, 255) 2px -2px 0px, rgb(255, 255, 255) 2px 2px 0px;"
White, in this example.
Does not work in IE :)

Trying to animate SVG gradient

I am using SVG to try and animate a gradient path - to create a tail / shooting star effect. In doing this, I want to animate an object at the front of the tail, which I have shown in my example below (the circles).
The example works barely in Google Chrome, haven't checked others... You can see I've got 5 circles/paths and only 1 of them is working properly. The curved one animates the gradient at a different speed to the object and the others don't work properly at all except for the "almost-horizontal" one.
Can someone please provide some insight into why this doesn't work, recommend a way I could do this and provide an example if possible?
I'm almost to the point where i'll just write my own render code in canvas and using a JS library... :(
<svg style="height: 400px; width: 100%" viewBox="0 0 500 200">
<path id="circlePath1" stroke-width="2" d="M10 100 Q 100 10, 150 80 T 300 100" stroke="url(#grad)" fill="transparent"></path>
<path id="circlePath2" stroke-width="2" d="M30 20 L 130 19" stroke="url(#grad)" fill="transparent"></path>
<path id="circlePath3" stroke-width="2" d="M30 10 L 130 10" stroke="url(#grad)" fill="transparent"></path>
<path id="circlePath4" stroke-width="2" d="M10 10 L 10 110" stroke="url(#grad)" fill="transparent"></path>
<path id="circlePath5" stroke-width="2" d="M10 20 L 20 110" stroke="url(#grad)" fill="transparent"></path>
<linearGradient id='grad'>
<stop stop-opacity="0" stop-color='#800'>
<animate attributeName="offset" dur="2s" values='-0.20;0.80' repeatCount="indefinite" ></animate>
</stop>
<stop stop-color='#800' stop-opacity=".5">
<animate attributeName="offset" dur="2s" values='-0.02;0.98' repeatCount="indefinite" ></animate>
</stop>
<stop stop-opacity="0.5" stop-color='#800'>
<animate attributeName="offset" dur="2s" values='-0;1' repeatCount="indefinite" ></animate>
</stop>
<stop stop-opacity="0" stop-color='#800'>
<animate attributeName="offset" dur="2s" values='-0;1' repeatCount="indefinite" ></animate>
</stop>
</linearGradient>
<circle id="c1" r="2.5" cx="" cy="" fill="#880000">
<animateMotion dur="2s" repeatCount="indefinite">
<mpath href="#circlePath1"></mpath>
</animateMotion>
</circle>
<circle id="c2" r="2.5" cx="" cy="" fill="#880000">
<animateMotion dur="2s" repeatCount="indefinite">
<mpath href="#circlePath2"></mpath>
</animateMotion>
</circle>
<circle id="c3" r="2.5" cx="" cy="" fill="#880000">
<animateMotion dur="2s" repeatCount="indefinite">
<mpath href="#circlePath3"></mpath>
</animateMotion>
</circle>
<circle id="c4" r="2.5" cx="" cy="" fill="#880000">
<animateMotion dur="2s" repeatCount="indefinite">
<mpath href="#circlePath4"></mpath>
</animateMotion>
</circle>
<circle id="c5" r="2.5" cx="" cy="" fill="#880000">
<animateMotion dur="2s" repeatCount="indefinite">
<mpath href="#circlePath5"></mpath>
</animateMotion>
</circle>
</svg>
In this attempt I use a radial gradient that is masked by the line to create the trail, then a separate dot.
<svg width="500" height="300" viewBox="0 0 500 300" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
<defs>
<path id="motion-path" stroke-width="2" d="M202.4 58.3c-13.8.1-33.3.4-44.8 9.2-14 10.7-26.2 29.2-31.9 45.6-7.8 22.2-13.5 48-3.5 70.2 12.8 28.2 47.1 43.6 68.8 63.6 19.6 18.1 43.4 26.1 69.5 29.4 21.7 2.7 43.6 3.3 65.4 4.7 19.4 1.3 33.9-7.7 51.2-15.3 24.4-10.7 38.2-44 40.9-68.9 1.8-16.7 3.4-34.9-10.3-46.5-9.5-8-22.6-8.1-33.2-14.1-13.7-7.7-27.4-17.2-39.7-26.8-5.4-4.2-10.4-8.8-15.8-12.9-4.5-3.5-8.1-8.3-13.2-11-6.2-3.3-14.3-5.4-20.9-8.2-5-2.1-9.5-5.2-14.3-7.6-6.5-3.3-12.1-7.4-19.3-8.9-6-1.2-12.4-1.3-18.6-1.5-10.2-.3-20.2-1.5-30.3-1" stroke="#666" fill="none"/>
<mask id="path-mask">
<use xlink:href="#motion-path" stroke="#666"/>
</mask>
<symbol id="ball">
<circle id="ball" r="2.5" fill="#800">
<animateMotion dur="5s" repeatCount="indefinite">
<mpath xlink:href="#motion-path"/>
</animateMotion>
</circle>
</symbol>
<symbol id="trail">
<circle r="30" fill="url(#grad)">
<animateMotion dur="5s" repeatCount="indefinite" rotate="auto">
<mpath xlink:href="#motion-path"/>
</animateMotion>
</circle>
</symbol>
<linearGradient id="grad">
<stop offset="0" stop-opacity="0" stop-color="#800"/>
<stop offset=".5" stop-opacity=".8" stop-color="#800"/>
<stop offset=".5" stop-opacity="0" stop-color="#800"/>
</linearGradient>
</defs>
<use xlink:href="#ball"/>
<use xlink:href="#trail" mask="url(#path-mask)"/>
</svg>
It has its limitations (if the path gets too close you get the trail showing on both bits) but hopefully this gives you an idea to play with.

Categories