Real-time Iteration over SVG objects - javascript

For example i have this in html
<svg id="svg-img1" width="100%" height="100%" version="1.1" xmlns="http://www.w3.org/2000/svg">
<line x1="0" y1="0" x2="0" y2="0" style="stroke:black; stroke-width:1px;"></line>
<line x1="0" y1="0" x2="0" y2="0" style="stroke:black; stroke-width:1px;"></line>
<line x1="0" y1="0" x2="0" y2="0" style="stroke:black; stroke-width:1px;"></line>
<line x1="0" y1="0" x2="0" y2="0" style="stroke:black; stroke-width:1px;"> </line>
</svg>
and this in my js
$('svg').find('line').each(function(){
$(this).attr('style','stroke:red');
sleep(10);
});
and "sleep" function
function sleep(ms) {
ms += new Date().getTime();
while (new Date() < ms){}
}
When executing, the color of my lines changes at once for everyone. And I need the color of the lines to change in turn, and so that it can be seen as one color after another changes color. Something similar to animation
Tried without jquery - the same result

Use the SVG animate element:
<animate id="a14" attributeName="stroke" from="black" to="red"
dur="3s" begin="9s;al4.end+3s" stroke="freeze" />
NOTE: Do not cut and paste the code above, it has been broken in the middle so that it can be seen without scrolling.
attributeName: The name of the attribute to animate
from: Starting value
to: Ending value
dur: Duration of animation
begin: When to begin; this particular set of values says: start in 9 seconds; (referenced this animate element by id) to end in 3 seconds
stroke: The attribute that's being animated, this attribute will stop after it has animated,
SNIPPET
<svg id="svg-img1" width="100%" height="100%" version="1.1" xmlns="http://www.w3.org/2000/svg">
<line x1="0" y1="10" x2="400" y2="10" style="stroke:black; stroke-width:1px;">
<animate id="a11" attributeName="stroke" from="black" to="red" dur="3s" begin="0s;al1.end+3s" stroke="freeze" />
</line>
<line x1="0" y1="20" x2="400" y2="20" style="stroke:black; stroke-width:1px;">
<animate id="a12" attributeName="stroke" from="=black" to="red" dur="3s" begin="3s;al2.end+3s" stroke="freeze" />
</line>
<line x1="0" y1="30" x2="400" y2="30" style="stroke:black; stroke-width:1px;">
<animate id="a13" attributeName="stroke" from="black" to="red" dur="3s" begin="6s;al3.end+3s" stroke="freeze" />
</line>
<line x1="0" y1="40" x2="400" y2="40" style="stroke:black; stroke-width:1px;">
<animate id="a14" attributeName="stroke" from="black" to="red" dur="3s" begin="9s;al4.end+3s" stroke="freeze" />
</line>
</svg>

Instead of sleep, you can use window.setTimeout to execute a function after a certain delay. So, you could do something like this:
$('svg').find('line').each(function(index, element){
window.setTimeout(function() {
$(element).attr('style','stroke:red');
}, index * 100);
});

Related

SVG animate pattern from top left

I got this simple SVG animation that is transforming the pattern from dots to circles on page load.
<svg width="596" height="255">
<pattern id="pattern-circles" x="0" y="0" width="20" height="20" patternUnits="userSpaceOnUse">
<circle cx="6" cy="6" r="3" stroke="red" stroke-width="2" fill="transparent">
<animate attributeName="r" values="0; 5" dur="2s" begin="0s" repeatCount="0" fill="freeze" />
</circle>
</pattern>
<!-- The canvas with our applied pattern -->
<rect x="0" y="0" width="100%" height="100%" fill="url(#pattern-circles)" />
</svg>
I am pretty new to SVG animation and have a hard time figure out how to achieve my goal, in addition to what I already got (dots to circles), to have this animation start/fade-in from the top left dot and end in the bottom right dot - is this even possible to achieve with this SVG pattern setup? Is there a way to isolate the dots, and stagger them in one-by-one?
Here is one idea where I will generate the circles using JS. On each iteration I increment the delay using i/j to create the to top-left to bottom-right animation. The position is trivial, the cx is based on i and cy on j
var d = 20;
var nx = 596/d; /* number of circles in a row */
var ny = 255/d; /* number of circles in a columns */
let svg = document.querySelector("svg");
for(var i=0;i<nx;i++) {
for(var j=0;j<ny;j++) {
svg.insertAdjacentHTML( 'beforeend','<circle cx="'+(6 + d*i)+'" cy="'+(6 + d*j)+'" r="0" stroke="red" stroke-width="2" fill="transparent"><animate attributeName="r" values="0; 5" dur="1s" begin="'+((i+j)/10)+'s" repeatCount="0" fill="freeze" /></circle>');
}
}
<svg width="596" height="255">
</svg>
To have a to-right animation keep only the i (same logic if you want a to-bottom one by keeping only the j)
var d = 20;
var nx = 596/d; /* number of circles in a row */
var ny = 255/d; /* number of circles in a columns */
let svg = document.querySelector("svg");
for(var i=0;i<nx;i++) {
for(var j=0;j<ny;j++) {
svg.insertAdjacentHTML( 'beforeend','<circle cx="'+(6 + d*i)+'" cy="'+(6 + d*j)+'" r="0" stroke="red" stroke-width="2" fill="transparent"><animate attributeName="r" values="0; 5" dur="1s" begin="'+(i/10)+'s" repeatCount="0" fill="freeze" /></circle>');
}
}
<svg width="596" height="255">
</svg>
An infinite animation:
var d = 20;
var nx = 596/d; /* number of circles in a row */
var ny = 255/d; /* number of circles in a columns */
let svg = document.querySelector("svg");
for(var i=0;i<nx;i++) {
for(var j=0;j<ny;j++) {
svg.insertAdjacentHTML( 'beforeend','<circle cx="'+(6 + d*i)+'" cy="'+(6 + d*j)+'" r="0" stroke="red" stroke-width="2" fill="transparent"><animate attributeName="r" values="0; 5;0" dur="2s" begin="'+((i+j)/20)+'s" repeatCount="indefinite" /></circle>');
}
}
<svg width="596" height="255">
</svg>
And why not from the center:
var d = 20;
var nx = 596/d; /* number of circles in a row */
var ny = 255/d; /* number of circles in a columns */
var ic = nx/2;
var jc = ny/2;
let svg = document.querySelector("svg");
for(var i=0;i<nx;i++) {
for(var j=0;j<ny;j++) {
svg.insertAdjacentHTML( 'beforeend','<circle cx="'+(6 + d*i)+'" cy="'+(6 + d*j)+'" r="0" stroke="red" stroke-width="2" fill="transparent"><animate attributeName="r" values="0; 5;0" dur="2s" begin="'
+( Math.sqrt((ic - i)*(ic - i)+(jc - j)*(jc - j))/20)+'s" repeatCount="indefinite" /></circle>');
}
}
<svg width="596" height="255">
</svg>
Add an animated mask with a linearGradient.
<svg width="596" height="255">
<linearGradient id="prog-mask" x1=0% x2="100%" y1="0%" y2="100%">
<stop offset="0%" stop-color="white" stop-opacity="1" />
<stop offset="5%" stop-color="white" stop-opacity="0">
<animate attributeName="offset" values="0; 1" dur="2s" begin="0s" repeatCount="0" fill="freeze" />
<animate attributeName="stop-opacity" values="0; 1" dur="2s" begin="2s" repeatCount="0" fill="freeze" />
</stop>
<stop offset="100%" stop-color="white" stop-opacity="0" />
</linearGradient>
<mask id="prog-render">
<rect x="0" y="0" width="100%" height="100%" fill="url(#prog-mask)"/>
</mask>
<pattern id="pattern-circles" x="0" y="0" width="20" height="20" patternUnits="userSpaceOnUse">
<circle cx="6" cy="6" r="3" stroke="red" stroke-width="2" fill="transparent">
<animate attributeName="r" values="0; 5" dur="2s" begin="0s" repeatCount="0" fill="freeze" />
</circle>
</pattern>
<!-- The canvas with our applied pattern -->
<rect x="0" y="0" width="100%" height="100%" fill="url(#pattern-circles)" mask="url(#prog-render)"/>
</svg>
You can fade things in with linearGradients. Here, the linearGradients disappear revealing the pattern below them.
Because we have two linearGradients the animation would look faster in the middle than at the start and end (we're multiplying two opacity numbers) so I'm using keySplines to make the animation faster in the middle and slower at the start and end to counteract that.
<svg width="596" height="255">
<defs>
<pattern id="pattern-circles" x="0" y="0" width="20" height="20" patternUnits="userSpaceOnUse">
<circle cx="6" cy="6" r="3" stroke="red" stroke-width="2" fill="none">
</circle>
</pattern>
<linearGradient id="g1" x1="100%" y1="0" x2="0" y2="0">
<stop offset="0%" stop-color="white">
<animate attributeName="stop-opacity" values="1; 0" calcMode="spline" keyTimes="0;1" keySplines="0.5 0 0.5 1" dur="2s" begin="0s" repeatCount="0" fill="freeze" />
</stop>
<stop offset="100%" stop-color="white" stop-opacity="0" />
</linearGradient>
<linearGradient id="g2" x1="0" y1="100%" x2="0" y2="0">
<stop offset="0%" stop-color="white" stop-opacity="1">
<animate attributeName="stop-opacity" values="1; 0" calcMode="spline" keyTimes="0;1" keySplines="0.5 0 0.5 1" dur="2s" begin="0s" repeatCount="0" fill="freeze" />
</stop>
<stop offset="100%" stop-color="white" stop-opacity="0" />
</linearGradient>
</defs>
<rect width="100%" height="100%" fill="url(#pattern-circles)" />
<rect width="100%" height="100%" fill="url(#g1)"/>
<rect width="100%" height="100%" fill="url(#g2)"/>
</svg>

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>

How to implement a html element inside a javascript code?

I am trying to design a sample web page, and I'm facing some issues with svg....
I have created an svg circle with a line over it which will rotate with in the circle. Here is the sample code.
<?xml version="1.0" encoding="iso-8859-1"?>
<svg width="100%" height="100%">
<circle cx="200" cy="200" r="100" stroke="black" stroke-width="4" fill="black" />
<g transform="translate(200,200)">
<line x1="0" y1="0" x2="100" y2="0" style="stroke:rgb(255,255,255);stroke-width:2">
<animateTransform attributeName="transform"
attributeType="XML"
type="rotate"
from="25" to="360" dur="100s"/>
</line>
</svg>
In the sample code above the 'line' will start rotating immediately when the page is launched, but I want the line to be rotated after I press the START button, something like this...
<input type="button" value=" START " onclick="start()" class="control-menu-top-btn" id="green-btn">
<script language="javascript" type="text/javascript">
function start()
{
.. ?
}
</script>
The beginElement method will start an animation. Setting the begin attribute to indefinite in the markup will stop it from running before the javascript starts it.
html, body {
width: 100%;
height: 100%;
}
<input type="button" value=" START " onclick="start()" class="control-menu-top-btn" id="green-btn">
<script>
function start()
{
document.getElementById("transform").beginElement();
}
</script>
<svg width="100%" height="100%">
<circle cx="200" cy="200" r="100" stroke="black" stroke-width="4" fill="black" />
<g transform="translate(200,200)">
<line x1="0" y1="0" x2="100" y2="0" style="stroke:rgb(255,255,255);stroke-width:2">
<animateTransform id="transform" attributeName="transform"
attributeType="XML"
type="rotate"
from="25" to="360" dur="100s" begin="indefinite" />
</line>
</g>
</svg>

how to unite path and change shape in svg

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>​

In SVG is there a way to grab a shape anywhere when it has fill = "none"

I have a series of shapes that I want the user to be able click anywhere on the shape to pick up as part of a drag and drop feature. All of these shapes are bounded by a rectangle.
For example:
<g id="shape1" fill="none" stroke="black">
<rect x="0" y="0" width="100" height="100"/>
<circle cx="50" cy="50" r="50"/>
</g>
<g id="shape2" fill="none" stroke="black">
<rect x="0" y="0" width="100" height="100"/>
<line x1="0" y1="0" x2="50" y2="100"/>
<line x1="100" y1="0" x2="50" y2="100"/>
</g>
I already have all of the drag and drop parts working, the problem is all of these shapes have to have fill="none" so you can see anything that may be underneath of them. This means that even though they are bounded by the rectangle, at the moment users have to physically click on one of the lines in order to pick it up instead of being able to click anywhere on the shape like I want.
My original idea was to use fill="white" and then set opacity="0" or some really low value but this applies to the stroke as well so this didn't work out.
Any ideas on how I can get this working?
Pointer-events is probably what you want here.
Here are some examples:
http://dev.w3.org/SVG/profiles/1.1F2/test/svg/interact-pevents-07-t.svg
http://dev.w3.org/SVG/profiles/1.1F2/test/svg/interact-pevents-08-f.svg
http://dev.w3.org/SVG/profiles/1.1F2/test/svg/interact-pevents-09-f.svg
So it turns out I was a lot closer then I thought. Who knew they had a fill-opacity attribute...
<g id="shape1" fill="white" stroke="black" fill-opacity="0">
<rect x="0" y="0" width="100" height="100"/>
<circle cx="50" cy="50" r="50"/>
</g>
<g id="shape2" fill="white" stroke="black" fill-opacity="0">
<rect x="0" y="0" width="100" height="100"/>
<line x1="0" y1="0" x2="50" y2="100"/>
<line x1="100" y1="0" x2="50" y2="100"/>
</g>

Categories