svg circle color change at certain point - svg

I have a svg that circle is moving around through path. I want the circles' color are changed at some points (ex. mid of the path)
https://codepen.io/lzwdct/pen/poRYVXZ
<circle r="20" fill="blue" mask="url(#myMask)">
<animateMotion dur="5s" repeatCount="indefinite"
path="M718.54,66.06L294.41,490.19c-48.89,48.89-128.09,48.95-176.91,0.13c-48.82-48.82-48.76-128.02,0.13-176.91
s128.09-48.95,176.91-0.13 M294.28,313.55l424.13,424.13c48.89,48.89,128.09,48.95,176.91,0.13c48.82-48.82,48.76-128.02-0.13-176.91
c-48.89-48.89-128.09-48.95-176.91-0.13" />
</circle>
Please guide me how to update the color. The color eventually will be changed multiple times with long paths.

I'm not sure if there's anything in SVG that would enable you to do that directly.
But you can check currentTime vs the duration of the animateMotion element and set a color via javascript based on that.
const color = (n) => {
const R = Math.round((255 * n) / 100);
const G = Math.round((255 * (100 - n)) / 100);
const B = 0;
return `rgb(${R}, ${G}, ${B})`;
};
const circles = document.querySelectorAll("circle");
window.setInterval(() => {
circles.forEach(circle => {
const ani = circle.querySelector('animateMotion');
const duration = ani.getSimpleDuration();
const currentTime = ani.getCurrentTime() % duration;
const colorValue = color((currentTime / duration) * 100);
circle.parentNode.style.fill = colorValue;
});
}, 500);
circle {
transition: 500ms;
}
<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" viewBox="0 0 2549.62 3338.72" style="enable-background:new 0 0 2549.62 3338.72;" xml:space="preserve">
<style type="text/css">
.st0{fill:none;stroke:#8EA5AE;stroke-width:50;stroke-miterlimit:10;}
.st1{fill:none;stroke:#8EA5AE;stroke-width:50;stroke-linecap:round;stroke-miterlimit:10;}
.st2{fill:none;stroke:#758992;stroke-width:50;stroke-miterlimit:10;}
.st3{fill:none;stroke:#758992;stroke-width:50;stroke-linecap:round;stroke-miterlimit:10;}
.st4{fill:none;stroke:#4E5F65;stroke-width:50;stroke-miterlimit:10;}
.st5{fill:none;stroke:#4E5F65;stroke-width:50;stroke-linecap:round;stroke-miterlimit:10;}
</style>
<g>
<mask id="myMask">
<!-- Pixels under white are rendered -->
<rect x="0" y="0" width="1015" height="855" fill="white" />
<!-- Pixels under black are hidden -->
<rect class="moveme" x="315" y="335" height="150" width="150" transform="rotate(45 395 395)">
<animateTransform attributeName="transform"
attributeType="XML"
type="scale"
keyTimes="0; 0.25999; 0.26; 1"
values="1; 1; 0; 0"
dur="5s"
additive="sum"
repeatCount="indefinite"/>
</rect>
</mask>
<g>
<path class="st0" d="M718.54,66.06L294.41,490.19c-48.89,48.89-128.09,48.95-176.91,0.13c-48.82-48.82-48.76-128.02,0.13-176.91
s128.09-48.95,176.91-0.13" />
<path class="st1" d="M683.19,30.7L258.92,454.97c-29.29,29.29-76.78,29.29-106.07,0c-29.29-29.29-29.29-76.78,0-106.07
c29.29-29.29,76.78-29.29,106.07,0"/>
<path class="st2" d="M753.9,101.42c0,0-424.26,424.26-424.26,424.26c-68.34,68.34-179.15,68.34-247.49,0s-68.34-179.15,0-247.49
s179.15-68.34,247.49,0"/>
</g>
<g>
<path class="st0" d="M294.28,313.55l424.13,424.13c48.89,48.89,128.09,48.95,176.91,0.13c48.82-48.82,48.76-128.02-0.13-176.91
c-48.89-48.89-128.09-48.95-176.91-0.13"/>
<path class="st2" d="M329.63,278.19L753.9,702.46c29.29,29.29,76.78,29.29,106.07,0c29.29-29.29,29.29-76.78,0-106.07
s-76.78-29.29-106.07,0"/>
<path class="st1" d="M258.92,348.9c0,0,424.26,424.26,424.26,424.26c68.34,68.34,179.15,68.34,247.49,0s68.34-179.15,0-247.49
s-179.15-68.34-247.49,0"/>
</g>
</g>
<g mask="url(#myMask)">
<circle r="20" mask="url(#myMask)">
<animateMotion dur="5s" repeatCount="indefinite"
path="M718.54,66.06L294.41,490.19c-48.89,48.89-128.09,48.95-176.91,0.13c-48.82-48.82-48.76-128.02,0.13-176.91
s128.09-48.95,176.91-0.13 M294.28,313.55l424.13,424.13c48.89,48.89,128.09,48.95,176.91,0.13c48.82-48.82,48.76-128.02-0.13-176.91
c-48.89-48.89-128.09-48.95-176.91-0.13" />
</circle>
<circle r="20">
<animateMotion dur="5s" repeatCount="indefinite"
path="M753.9,101.42c0,0-424.26,424.26-424.26,424.26c-68.34,68.34-179.15,68.34-247.49,0s-68.34-179.15,0-247.49
s179.15-68.34,247.49,0 M329.63,278.19L753.9,702.46c29.29,29.29,76.78,29.29,106.07,0c29.29-29.29,29.29-76.78,0-106.07 s-76.78-29.29-106.07,0" />
</circle>
<circle r="20">
<animateMotion id="circle1" dur="5s" repeatCount="indefinite"
path="M683.19,30.7L258.92,454.97c-29.29,29.29-76.78,29.29-106.07,0c-29.29-29.29-29.29-76.78,0-106.07 c29.29-29.29,76.78-29.29,106.07,0 M258.92,348.9c0,0,424.26,424.26,424.26,424.26c68.34,68.34,179.15,68.34,247.49,0s68.34-179.15,0-247.49 s-179.15-68.34-247.49,0" /></circle></g>
</svg>

You need to tweak it for your paths, basic concept is to add an animate on the element you want to change and use values and keyTimes (the whole animation is 0 to 1)
As you will notice, you get a color-gradient for free.
If you don't want that, add more keyTimes so the color fades occur faster
<svg width="450" height="180">
<path id="PATH" d="M 50 90 H400" stroke="black"/>
<g>
<circle class="circle" r="30" fill="black"></circle>
<circle class="circle" r=25 fill="red" >
<animate
attributeName="fill"
attributeType="XML"
values="red;green;yellow;hotpink;blue"
keyTimes= "0;0.4;0.6;0.8;1"
dur="3s"
repeatCount="indefinite"/>
</circle>
<animateMotion dur="3s" repeatCount="indefinite">
<mpath href="#PATH" />
</animateMotion>
</g>
</svg>
svga

Related

SVG hide an object until needed

I'm able to move an object along a Bezier Curve but am having some difficulty with 'defs' and 'use'. My object (circle) appears at 0,0 before the animation begins, then appears in the correct position.
<svg viewBox="0 0 500 300" style="border:1px solid black; width:500; height:500;" xmlns="http://www.w3.org/2000/svg" >
<path id="track" d="M100,200 C200,150 300,250 400,200" stroke-width="3" stroke="#000" fill="none"/>
<circle id="circ1" r="10" cx="0" cy="0" fill="red" >
<animateMotion begin="1s" dur="6s" fill="freeze">
<use href="#circ1" cx="100" cy="200"/>
<mpath xlink:href="#track"/>
</animateMotion>
</circle>
</svg>
How can I make it appear at the beginning of the Bezier line, not at 0,0?
You can use <set> elements to set the position of the circle at the begining of the curve. Next at 1s you need to set again the position of the circle on 0,0
<svg viewBox="0 50 500 300" style="border:1px solid black; width:500; height:300;" xmlns="http://www.w3.org/2000/svg" >
<path id="track" d="M100,200 C200,150 300,250 400,200" stroke-width="3" stroke="#000" fill="none"/>
<circle id="circ1" r="10" cx="0" cy="0" fill="red">
<set begin="0" attributeName="cx" to="100" />
<set begin="0" attributeName="cy" to="200" />
<set begin="1s" attributeName="cx" to="0" />
<set begin="1s" attributeName="cy" to="0" />
<animateMotion begin="1s" dur="6s" fill="freeze">
<!--<use href="#circ1" cx="100" cy="200"/>-->
<mpath xlink:href="#track"/>
</animateMotion>
</circle>
</svg>
Yet another solution would be changing the origin of the curve so that it begins in the 0,0.
Please observe that I've changed the value of the viewBox attribute so that you can still see the curve in the middle of the svg canvas.
<svg viewBox="-100 -150 500 300" style="border:1px solid black; width:500; height:300;" xmlns="http://www.w3.org/2000/svg" >
<path id="track" d="M0,0C100,-50,200,50,300,0" stroke-width="3" stroke="#000" fill="none"/>
<circle id="circ1" r="10" cx="0" cy="0" fill="red" >
<animateMotion begin="1s" dur="6s" fill="freeze">
<!--<use href="#circ1" cx="100" cy="200"/>-->
<mpath xlink:href="#track"/>
</animateMotion>
</circle>
</svg>

Is possible center <g> in <svg>?

I searched on the web and I do not found a solution for my problem, is possible to do that, center my element depending of size of my parent content.
<svg width="100%" height="100%" xmlns="http://www.w3.org/2000/svg" stroke="#333">
<g fill="none" fill-rule="evenodd">
<g transform="translate(1 1)" stroke-width="2">
<circle stroke-opacity=".5" cx="18" cy="18" r="18"/>
<path d="M36 18c0-9.94-8.06-18-18-18">
<animateTransform
attributeName="transform"
type="rotate"
from="0 18 18"
to="360 18 18"
dur="1s"
repeatCount="indefinite"/>
</path>
</g>
</g>
</svg>
I would put the loader in a symbol. Also I would give the loader and the svg element a viewBox attribute.
svg{border:1px solid;}
<svg width="100%" viewBox="-250 -50 500 100" xmlns="http://www.w3.org/2000/svg" stroke="#333">
<symbol id="the_symbol" viewBox="-2 -2 40 40">
<g stroke-width="2">
<circle stroke-opacity=".5" cx="18" cy="18" r="18"/>
<path d="M36 18A18,18 0 0 0 0,18">
<animateTransform
attributeName="transform"
type="rotate"
from="0 18 18"
to="360 18 18"
dur="1s"
repeatCount="indefinite"/>
</path>
</g>
</symbol>
<use xlink:href="#the_symbol" x="-20" y="-20" width="36" height="36" fill="none" />
</svg>
UPDATE
The OP is commenting that they want the root svg to take various sizes, while the loader has to be always the same size.
//the size and position of the symbol
let o = {x:36,y:18}
function init(){
let p = getsize(o);
//reset the size and position of the use element
theUse.setAttributeNS(null,"width",p.x)
theUse.setAttributeNS(null,"height",p.x)
theUse.setAttributeNS(null,"x",250-p.y)
theUse.setAttributeNS(null,"y",50-p.y)
}
// a function to calculate the size and position of the use element
function getsize(o){
var p = svg.createSVGPoint();
p.x= o.x;
p.y= o.y;
p = p.matrixTransform(svg.getScreenCTM().inverse());
return p
}
setTimeout(function() {
init();
addEventListener('resize', init, false);
}, 15);
svg{border:1px solid;}
symbol{overflow:visible}
<svg id="svg" width="100%" height="100%" viewBox="0 0 500 100" xmlns="http://www.w3.org/2000/svg" stroke="#333">
<symbol id="the_symbol" viewBox="0 0 36 36">
<g stroke-width="2">
<circle stroke-opacity=".5" cx="18" cy="18" r="18"/>
<path d="M36 18A18,18 0 0 0 0,18">
<animateTransform
attributeName="transform"
type="rotate"
from="0 18 18"
to="360 18 18"
dur="1s"
repeatCount="indefinite"/>
</path>
</g>
</symbol>
<use id="theUse" xlink:href="#the_symbol" fill="none" />
</svg>

animateMotion a path with marker-start and marker-end svg only

is it possible to animate a path with marker elements? the path id in question is #orbit1. the reason i created the markers is that they will be used multiple times.
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1" viewBox="0 0 1500 1000">
<defs>
<marker id="arrowRight" viewBox="0 0 7.1 11.5" refX="5" refY="5.75"
markerUnits="userSpaceOnUse" orient="auto-start-reverse"
markerWidth="7.1" markerHeight="11.5">
<polygon points="1,11.5 0,10.4 5.1,5.7 0,1 1,0 7.1,5.7" fill="#00897b"/>
</marker>
<marker id="circle" viewBox="0 0 6 6" refX="1" refY="3"
markerUnits="userSpaceOnUse" orient="auto"
markerWidth="6" markerHeight="6">
<circle cx="3" cy="3" r="3" fill="#4caf50"/>
</marker>
</defs>
<!-- arrowhead symbol -->
<symbol>
<path id="d_arrow1" fill="red" d="M-10-10L10 0l-20 10 2-10-2-10z" />
</symbol>
<!-- animateMotion defines the motion path animation -->
<animateMotion xlink:href="#a1" begin="0s" dur="3s" rotate="auto" repeatCount="indefinite">
<mpath xlink:href="#orbit1" />
<!-- mpath = sub-element for the <animateMotion> element provides the ability to reference an external <path> element as the definition of a motion path -->
</animateMotion>
<animateMotion xlink:href="#a2" begin="0s" dur="3s" rotate="auto-reverse" keyPoints="1;0" keyTimes="0;1" calcMode="linear" repeatCount="indefinite">
<mpath xlink:href="#orbit2" />
</animateMotion>
<!-- arrow paths -->
<path id="orbit2" d='M 365 400 A 370 200 0 0 1 1100 400' fill="none" stroke-width="3" style="stroke: green; stroke-dasharray: 50 818; stroke-dashoffset: 50"/>
<!-- marker arrows -->
<!-- orbit1 to be animated using mpath -->
<path id="orbit1" d="M308.7 34.9C381.3 37.4 444.3 78 478.7 137.5" stroke="#4caf50" fill="none" stroke-width="2" stroke-miterlimit="10" stroke-dasharray="50 1000" stroke-dashoffset="50"
marker-start="url(#circle)"
marker-end="url(#arrowRight)"/>
<!-- <animate> svg element used to animate an attribute or property of an element over time. it's normally inserted inside the element or referenced by the href attribute of the target element -->
<animate id="anim1" xlink:href="#orbit1" attributeName="stroke-dashoffset" from="50" to="-960" dur="3s" begin="0s" fill="freeze" repeatCount="indefinite" />
<!-- animate using the animateMotion definition above -->
<animate id="anim2" xlink:href="#orbit2" attributeName="stroke-dashoffset" from="-810" to="50" dur="3s" begin="0s" fill="freeze" repeatCount="indefinite" />
<!-- <use id="a1" xlink:href="#d_arrow1" x="0" y="0" width="100" height="50" />-->
<use id="a2" xlink:href="#d_arrow1" x="0" y="0" width="100" height="50" />
</svg>

Creating multiple random Circles in SVG

I am trying to create a pattern of multiple circles of different colour and each time created rotated slightly, here is an example of what I am trying to achieve:
http://etc.usf.edu/clipart/42900/42923/circle-29_42923.htm
I have created the svg to create the circle in the correct spacing however I am not sure how to duplicate the circle using SVG, here is a fragment of my code so far:
<title>Multiple Circles</title>
<svg x="100" >
<svg height="500" width="500">
<circle cx="100" cy="100" r="40" stroke="black" stroke-width="3" fill="transparent" />
</svg>
</svg>
</svg>
i have just created the design you mention in you link. i have used Javascript to achieve this.
here is fiddle , check it :- http://jsfiddle.net/aBaFf/
Steps:-
1) First i created a 'Main Circle' , around which i am going to create Rings of Circle.
2) I use 'Path' to create Circle. reason is to find exact points on the circular path.
3) i start created circle on that points i found out in Step 2.
check the below code:-
HTML Code:-
<svg id="mainSvg" width="1000" height="600">
<path id="smallCircle" fill="none" stroke="red"
d="
M 200, 200
m -75, 0
a 75,75 0 1,0 150,0
a 75,75 0 1,0 -150,0
"
/>
<path id="LargeCircle" fill="none" stroke="green"
d="
M 500, 200
m -150, 0
a 150,150 0 1,0 300,0
a 150,150 0 1,0 -300,0
"
/>
</svg>
JavaScript Code:-
function path2Array (path) {
var pointsArray = new Array();
for (var i = 0; i < path.getTotalLength(); i++) {
var point = path.getPointAtLength(i);
var cordinatesXY = {
x: point.x,
y: point.y
}
pointsArray.push(cordinatesXY);
}
return pointsArray;
};
var mainSvg=document.getElementById('mainSvg');
function createCircleRing(radius,gape,parent){
var mainCircle=document.getElementById(parent);
var points=path2Array(mainCircle);
var counter=0;
var circle;
for(var point in points){
counter++;
if(counter%gape===0){
point=points[point];
circle=document.createElementNS("http://www.w3.org/2000/svg", "circle");
circle.setAttribute("cx", point.x);
circle.setAttribute("cy", point.y);
circle.setAttribute("r", radius);
circle.setAttribute("fill", "none");
circle.setAttribute("stroke", "black");
mainSvg.appendChild(circle);
}
}
}
createCircleRing(10,5,'smallCircle');
createCircleRing(50,10,'LargeCircle');
If I wanted to reproduce this picture quickly, here is how I would do it.
<svg height="500" width="500" xmlns:xlink="http://www.w3.org/1999/xlink">
<g transform="translate(250,250)" stroke="black" stroke-width="3" fill="none">
<circle r="200" />
<circle id="c1" cy="130" r="70"/>
<use xlink:href="#c1" transform="rotate(15)"/>
<use xlink:href="#c1" transform="rotate(30)"/>
<use xlink:href="#c1" transform="rotate(45)"/>
<use xlink:href="#c1" transform="rotate(60)"/>
<use xlink:href="#c1" transform="rotate(75)"/>
<use xlink:href="#c1" transform="rotate(90)"/>
<use xlink:href="#c1" transform="rotate(105)"/>
<use xlink:href="#c1" transform="rotate(120)"/>
<use xlink:href="#c1" transform="rotate(135)"/>
<use xlink:href="#c1" transform="rotate(150)"/>
<use xlink:href="#c1" transform="rotate(165)"/>
<use xlink:href="#c1" transform="rotate(180)"/>
<use xlink:href="#c1" transform="rotate(195)"/>
<use xlink:href="#c1" transform="rotate(210)"/>
<use xlink:href="#c1" transform="rotate(225)"/>
<use xlink:href="#c1" transform="rotate(240)"/>
<use xlink:href="#c1" transform="rotate(255)"/>
<use xlink:href="#c1" transform="rotate(270)"/>
<use xlink:href="#c1" transform="rotate(285)"/>
<use xlink:href="#c1" transform="rotate(300)"/>
<use xlink:href="#c1" transform="rotate(315)"/>
<use xlink:href="#c1" transform="rotate(330)"/>
<use xlink:href="#c1" transform="rotate(345)"/>
</g>
</svg>
Fiddle here

how to repeat infinitely the whole svg

I mean after my SVG stops completing the animation I want the animation to start over again and end and then again start over and so on.
I tried:
<svg>
<animate repeatCount="indefinite"/>
</svg>
But it doesn't work.
See example animMotion01 in the w3 SVG spec for a fairly reduced example illustrating SVG animation (including use of repeatCount="indefinite"):
<svg width="5cm" height="3cm" viewBox="0 0 500 300"
xmlns="http://www.w3.org/2000/svg" version="1.1"
xmlns:xlink="http://www.w3.org/1999/xlink" >
<desc>Example animMotion01 - demonstrate motion animation computations</desc>
<rect x="1" y="1" width="498" height="298"
fill="none" stroke="blue" stroke-width="2" />
<!-- Draw the outline of the motion path in blue, along
with three small circles at the start, middle and end. -->
<path id="path1" d="M100,250 C 100,50 400,50 400,250"
fill="none" stroke="blue" stroke-width="7.06" />
<circle cx="100" cy="250" r="17.64" fill="blue" />
<circle cx="250" cy="100" r="17.64" fill="blue" />
<circle cx="400" cy="250" r="17.64" fill="blue" />
<!-- Here is a triangle which will be moved about the motion path.
It is defined with an upright orientation with the base of
the triangle centered horizontally just above the origin. -->
<path d="M-25,-12.5 L25,-12.5 L 0,-87.5 z"
fill="yellow" stroke="red" stroke-width="7.06" >
<!-- Define the motion path animation -->
<animateMotion dur="6s" repeatCount="indefinite" rotate="auto" >
<mpath xlink:href="#path1"/>
</animateMotion>
</path>
</svg>

Resources