How to animate an SVG circle around an ellipse - svg

This may be a duplicate of a similar question asked by Ruby several years ago, but following the suggestions posted has not resulted in a solution for me. Of course it could be me that is the problem.
I am trying to generate cycloid curves (circles rotating around other curves) BUT I would like to create the curve of a circle rotating around an ellipse - not a circle.
I can animate a circle to follow an elliptical path.
I can animate a circle to rotate about its center.
I need to do both animations simultaneously, but my object follows a path somewhere else in my viewbox.
html, body, svg {
width: 100%;
height: 100%;
}
<svg viewBox="0 0 700 700">
<path id="OUT" d="M200,250
a220,110 0 1,0 600,0
a220,110 0 1,0 -600,0"
style="fill:none;stroke:#ccc; stroke-width:2" />
<g id="CC">
<circle r=40 cx=200 cy=250 fill="none" stroke="black" stroke- width="2"/>
<g id="C1">
<circle r=5 cx=200 cy=250 fill="green"/>
</g>
<circle cx="240" cy="250" r="3" fill="blue" />
</g>
<animateTransForm
xlink:href="#CC"
attributeName="transform"
attributeType="XML"
type="rotate"
from="0 200 250" to="360 200 250" begin="1s" dur="10s"
additive="sum" />
<animateTransform
xlink:href="#CC"
attributeName="transform"
attributeType="XML"
type="rotate"
mpath xlink:href="#OUT"
from="0 300 250" to="360 300 250"
begin="1s" dur="10s" fill="freeze"
additive="sum"/>
</svg>
Hopefully this will give someone an idea of my effort, and trigger a solution. Many thanks.
www.softouch.on.ca

you just need to use animateMotion to apply line animation on any
shape just set so animation can know in which line it has to walk
because of transform translate issue i manually set transform
translate, it will work without setting transform translate in
circle
i used css key frame animation to rotate small circle and both
animation not working at a time so i added one layer(as <g> tag)
inside main <g> tag
html, body, svg {
width: 100%;
height: 100%;
}
<svg width="500" height="500" viewBox="0 0 1000 1000" >
<def>
<style>
#-webkit-keyframes rotation {
from {
-webkit-transform: rotate(0deg);
}
to {
-webkit-transform: rotate(359deg);
}
}
#extrag{
transform-origin: left;
animation: rotation 5s infinite linear;
transform-origin: 201px 250px;
}
</style>
</def>
<path id="OUT" d="M200,250
a220,110 0 1,0 600,0
a220,110 0 1,0 -600,0"
style="fill:none;stroke:#ccc; stroke-width:2" />
<g id="CC" transform="translate(-190,-245)">
<g id="extrag">
<circle r=40 cx=200 cy=250 fill="none" stroke="black" stroke-width="2"/>
<g id="C1" >
<circle r=5 cx=200 cy=250 fill="green"/>
</g>
<circle cx="240" cy="250" r="3" fill="blue">
</circle>
</g>
<animateMotion
xlink:href="#CC"
attributeName="motion"
attributeType="XML"
additive="sum"
dur="6s"
repeatCount="indefinite">
<mpath xlink:href="#OUT"/>
</animateMotion>
</g>
</svg>

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>

svg circle color change at certain point

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

SVG circle move(animateMotion) under the path

I have to write a code that circles should go under some paths with below SVG
<g>
<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"/>
</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"/>
</g>
</g>
<circle r="20" fill="blue">
<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" />
https://codepen.io/lzwdct/pen/poRYVXZ
Imagine the paths are like driveway, and the circle passes under the path(like bridge) it should not appear under the bridge.
Is there any way to implement it?
The way SVG mask works is a bit strange. The element it's applied to will only be rendered where the mask is white and where the mask is black (or just not white) it will be hidden. Another strange effect of SVG masks is that if you are animating an element and apply a mask to the element being animated then the mask will move with the element.
To account for the first part is simple, just add a white rect the size of the SVG itself inside the mask and use smaller black shapes to mask. The way to work around the moving mask is to apply the mask not to the element being animated but to a <g> tag that wraps the element(s) being animated.
If you want the circles to go 'under' a section and then 'over' that same section however, then you'll need to do some animating inside the mask as well. In this example I'm using animateTransform inside the mask's rect child (the black part that does the masking) to shrink it after the circles pass 'under' the bridge, you could just as easily use CSS keyframes though.
I suggest strongly that you also cut down the viewbox since your visual elements are so small compared to the available space, in the example I just estimated but the best way is to re-render your graphics in Illustrator and crop the artboard better to your objects.
Also most of the additional markup produced by Illustrator is not needed if the SVG will be inline in HTML. You can lose pretty much everything except the viewBox as shown in my example since those other attributes are mostly only used when the SVG is rendered as an image, hope this helps.
svg {
max-width: 500px;
}
.st0,
.st1 {
fill: none;
stroke: #8ea5ae;
stroke-width: 50;
stroke-miterlimit: 10;
}
.st1 {
stroke-linecap: round
}
.st2 {
fill: none;
stroke: #758992;
stroke-width: 50;
stroke-miterlimit: 10;
}
<svg viewBox="0 0 1015 855">
<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>
<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" />
<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" />
<!-- Group the circles and apply the mask to the group, not the circles -->
<g mask="url(#myMask)">
<circle r="20" fill="blue">
<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" fill="blue">
<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" fill="blue">
<animateMotion 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>
<!-- uncomment the rect below to visualize the animation applied to the mask -->
<!-- <rect x="315" y="335" height="150" width="150" fill="#f00" opacity=".1" 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>-->
</svg>

Why does my circle's marker not appear fully?

source: https://codepen.io/joondoe/pen/yLBMXPX?editors=1100
I would know why my circle fails to appear entirely since there is space place for it to display fully.
#import url('https://fonts.googleapis.com/css?family=PT+Sans');
body {
font-family: 'PT Sans', sans-serif;
padding: 2rem;
}
<h2>Beginning SVG: Drawing paths and marker</h2>
<svg width="1000" height="1000" >
<defs>
<marker
id="circle1" markerWidth="385" markerHeight="359"
refX="15" refY="5"
orient="auto"
>
<circle cx="15" cy="5" r="13" fill="black"/>
<circle cx="15" cy="5" r="12" fill="lightgray"/>
<path d="M 4,3.5 L 6.5,5 L4,6.5 Z" fill="slategray"/>
</marker>
</defs>
<line
x1="100" y1="220" x2="250" y2="200"
stroke="black" stroke-width="5"
marker-end="url(#circle1)"
/>
</svg>
Markers have a rectangular clipping area at the bounds of their tile. That's 0,0 to 385, 359 in your case so you need to draw the marker within that rect.
A circle with a cy="5" and r="13" will render above the y axis and therefore outside the clipping rect.
We can simply translate the marker contents and then move the marker refY to compensate. I've set the markerWidth and markerHeight at more sensible values too as having them too large prevents the browser from optimising marker rendering.
#import url('https://fonts.googleapis.com/css?family=PT+Sans');
body {
font-family: 'PT Sans', sans-serif;
padding: 2rem;
}
<h2>Beginning SVG: Drawing paths and marker</h2>
<svg width="1000" height="1000" viewBox="0 0 2000 2000">
<defs>
<marker
id="circle1" markerWidth="40" markerHeight="40"
refX="15" refY="25"
orient="auto"
>
<g transform="translate(0,20)">
<circle cx="15" cy="5" r="13" fill="black"/>
<circle cx="15" cy="5" r="12" fill="lightgray"/>
<path d="M 4,3.5 L 6.5,5 L4,6.5 Z" fill="slategray"/>
</g>
</marker>
</defs>
<line
x1="100" y1="220" x2="250" y2="200"
stroke="black" stroke-width="5"
marker-end="url(#circle1)"
/>
</svg>

Origin of CSS transform for svg:use

Is there a way to set the origin of CSS animation (transform - rotation) relatively to x,y of the element <use/> with CSS?
There might be several <use/> in a given <svg/> at different places:
absolute position of each element might not be hard coded in CSS.
.spin {
transition:4s linear;
-webkit-transition:4s linear;
}
.spin:hover {
transform-origin: 0% 0%;
-webkit-transform-origin: 0% 0%;
transform:rotateZ(360eg);
-webkit-transform:rotateZ(360deg);
}
<body>
<svg width="200" height="200" viewBox="0 0 200 200" xmlns:svg="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" >
<defs>
<path id="shape1" d="M0,0 V20 H20 Z" fill="red" stroke="red" stroke-width="0" />
</defs>
<circle cx="0" cy="0" r="80" fill="none" stroke="green"/>
<use xlink:href="#shape1" x="40" y="40" class="spin" />
</svg>
</body>
Animating the transform of a <use> is complicated by the fact that you are using the x and y attributes. The spec says that:
The x and y properties define an additional transformation (translate(x,y), ...
This extra transform component can mess with the transform you are animating, and produce unexpected effects.
The simple fix is to remove the x and y attributes, and use a parent <g> element to set the position of the path.
<g transform="translate(40,40)">
<use xlink:href="#shape1" class="spin" />
</g>
Updated example
.spin {
transition:4s linear;
-webkit-transition:4s linear;
}
.spin:hover {
transform-origin: 0px 0px;
-webkit-transform-origin: 0px 0px;
transform:rotateZ(360eg);
-webkit-transform:rotateZ(360deg);
}
<body>
<svg width="200" height="200" viewBox="0 0 200 200" xmlns:svg="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" >
<defs>
<path id="shape1" d="M0,0 V20 H20 Z" fill="red" stroke="red" stroke-width="0" />
</defs>
<circle cx="0" cy="0" r="80" fill="none" stroke="green"/>
<g transform="translate(40,40)">
<use xlink:href="#shape1" class="spin" />
</g>
</svg>
</body>

Resources