How can move multiple circles along a <mpath>
(motion path) using SVG SMIL Animation <<animateMotion>
.
Problem: About the first 3 4 circles everything was fine.
Some circles went "out of orbit" – so they are not correctly aligned with the motion path.
.planePath {
stroke: red;
stroke-width: .1%;
stroke-width: .5%;
stroke-dasharray: 1% 2%;
stroke-linecap: round;
fill: none;
z-index: 99;
}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="index.css">
<!-- CSS only -->
<link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" rel="stylesheet"
integrity="sha384-gH2yIJqKdNHPEq0n4Mqa/HGKIhSkIHeL5AyhkYV8i59U5AR6csBvApHHNl/vI1Bx" crossorigin="anonymous">
<title>Document</title>
</head>
<body>
<div class="container-fluid center" style="z-index: 99">
<svg viewBox="-300 -150 3387 1270" align="center" class="svg-animation">
<path id="planePath" class="planePath"
d="M1.50024 430C58.2002 -111.6 853.699 -156.741 889.5 430C925.5 1020 1754 1007.5 1785 430C1816 -147.5 2665.5 -132 2665.5 430C2665.5 1010.27 1847 948 1785 453C1841.5 -83.5 930.282 -187.244 889.5 389C851 933 35 1017.5 1.50024 430Z"/>
/>
<path style="position:absolute" id="circle2" class="planePath "
d="M1.50024 430C58.2002 -111.6 853.699 -156.741 889.5 430C925.5 1020 1754 1007.5 1785 430C1816 -147.5 2665.5 -132 2665.5 430C2665.5 1010.27 1847 948 1785 453C1841.5 -83.5 930.282 -187.244 889.5 389C851 933 35 1017.5 1.50024 430Z"/>
/>
<defs>
<filter id="filter0_d_0_1" x="0" y="17" width="897" height="847" filterUnits="userSpaceOnUse"
color-interpolation-filters="sRGB">
<feFlood flood-opacity="0" result="BackgroundImageFix" />
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0"
result="hardAlpha" />
<feOffset dy="4" />
<feGaussianBlur stdDeviation="2" />
<feComposite in2="hardAlpha" operator="out" />
<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.25 0" />
<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow_0_1" />
<feBlend mode="normal" in="SourceGraphic" in2="effect1_dropShadow_0_1" result="shape" />
</filter>
<filter id="filter1_d_0_1" x="926" y="33" width="897" height="847" filterUnits="userSpaceOnUse"
color-interpolation-filters="sRGB">
<feFlood flood-opacity="0" result="BackgroundImageFix" />
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0"
result="hardAlpha" />
<feOffset dy="4" />
<feGaussianBlur stdDeviation="2" />
<feComposite in2="hardAlpha" operator="out" />
<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.25 0" />
<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow_0_1" />
<feBlend mode="normal" in="SourceGraphic" in2="effect1_dropShadow_0_1" result="shape" />
</filter>
<filter id="filter2_d_0_1" x="1884" y="33" width="897" height="847" filterUnits="userSpaceOnUse"
color-interpolation-filters="sRGB">
<feFlood flood-opacity="0" result="BackgroundImageFix" />
<feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0"
result="hardAlpha" />
<feOffset dy="4" />
<feGaussianBlur stdDeviation="2" />
<feComposite in2="hardAlpha" operator="out" />
<feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.25 0" />
<feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow_0_1" />
<feBlend mode="normal" in="SourceGraphic" in2="effect1_dropShadow_0_1" result="shape" />
</filter>
</defs>
<g id="plane">
<circle cx="80" cy="0" r="20" fill="black" />
</g>
<g id="point">
<circle cx="-50" cy="0" r="20" fill="black"/>
</g>
<g id="point-2">
<circle cx="20" cy="0" r="20" fill="black" />
</g>
<g id="point-3">
<circle cx="-120" cy="0" r="20" fill="black" />
</g>
<g id="point-4">
<circle cx="140" cy="0" r="20" fill="black" />
</g>
<g id="point-5">
<circle cx="180" cy="20" r="20" fill="orange" />
</g>
<g id="point-6">
<circle cx="-180" cy="0" r="20" fill="black" />
</g>
<g id="point-7">
<circle cx="-200" cy="0" r="20" fill="black" />
</g>
<g id="point-8">
<circle cx="-220" cy="0" r="20" fill="black" />
</g>
<g id="point-9">
<circle cx="-240" cy="0" r="20" fill="black" />
</g>
<g id="point-10">
<circle cx="-260" cy="0" r="20" fill="black" />
</g>
<g id="point-11">
<circle cx="-280" cy="0" r="20" fill="black" />
</g>
<g id="point-12">
<circle cx="-300" cy="0" r="20" fill="black"/>
</g>
<g id="point-13">
<circle cx="320" cy="0" r="20" fill="black" />
</g>
<g id="point-14">
<circle cx="-340" cy="0" r="20" fill="black" />
</g>
<g id="point-15">
<circle cx="-360" cy="0" r="20" fill="black" />
</g>
<g id="point-16">
<circle cx="-380" cy="0" r="20" fill="black" />
</g>
<g id="point-17">
<circle cx="-400" cy="0" r="20" fill="black" />
</g>
<g id="point-18">
<circle cx="-420" cy="0" r="20" fill="black" />
</g>
<g id="point-19">
<circle cx="-430" cy="0" r="20" fill="black" />
</g>
<g id="point-20">
<circle cx="-440" cy="0" r="20" fill="black" />
</g>
<g id="point-21">
<circle cx="-460" cy="0" r="20" fill="black" />
</g>
<g id="point-22">
<circle cx="-480" cy="0" r="20" fill="black" />
</g>
<!-- Define the motion path animation -->
<animateMotion xlink:href="#plane" dur="20s" repeatCount="indefinite" rotate="auto">
<mpath xlink:href="#planePath" />
</animateMotion>
<animateMotion xlink:href="#point" dur="20s" repeatCount="indefinite" rotate="auto">
<mpath xlink:href="#planePath" />
</animateMotion>
<animateMotion xlink:href="#point-2" dur="20s" repeatCount="indefinite" rotate="auto">
<mpath xlink:href="#planePath" />
</animateMotion>
<animateMotion xlink:href="#point-3" dur="20s" repeatCount="indefinite" rotate="auto">
<mpath xlink:href="#planePath" />
</animateMotion>
<animateMotion xlink:href="#point-4" dur="20s" repeatCount="indefinite" rotate="auto">
<mpath xlink:href="#planePath" />
</animateMotion>
<animateMotion xlink:href="#point-5" dur="20s" repeatCount="indefinite" rotate="auto">
<mpath xlink:href="#planePath" />
</animateMotion>
<!-- <animateMotion xlink:href="#point-6" dur="20s" repeatCount="indefinite" rotate="auto">
<mpath xlink:href="#planePath" />
</animateMotion>
<animateMotion xlink:href="#point-7" dur="20s" repeatCount="indefinite" rotate="auto">
<mpath xlink:href="#planePath" />
</animateMotion>
<animateMotion xlink:href="#point-8" dur="20s" repeatCount="indefinite" rotate="auto">
<mpath xlink:href="#planePath" />
</animateMotion>
<animateMotion xlink:href="#point-9" dur="20s" repeatCount="indefinite" rotate="auto">
<mpath xlink:href="#planePath" />
</animateMotion> -->
</svg>
</div>
<!-- <div class="box">
<div class="circle">
</div>
<div class="circle">
</div>
<div class="circle">
</div>
</div> -->
<script src="index.js"></script>
</body>
</html>
<!-- begin snippet: js hide: false console: true babel: false -->
Your animated circles (moving along the motion path)
should be placed at cx/cy =0.
Explained here by @Paul LeBeau: Offset when following svg motion path
Otherwise their initial position will be added to the current motion path position.
That's why your circle are moving as in straight line around the path.
Path offset via animation delay
Actually all circles have the same center position of
cx="0"
cy="0"
- so they would be overlapping without animation.By adding an incremental
begin
value we mimic a path offset like so:The higher the
begin="0.1s"
value, the larger the distance between the circles.We can use also use negative
begin
values to set the distance between elements.The
begin
value is calculated like so:delay-increment: -0.1 * total number of circles.
Simplified example
Initial motion path offset – css
offset-path
to the rescueDisclaimer: browser support might still be spotty.
See also MDN Docs.
The main benefit of the
offset-path
property is its ability to actually define a start offset – pretty neat for static element renderings as well.(Quite similar to svg's
textPath
relatedstartOffset
property)In the above example the initial motion path offset is set by:
Also referring to a
@keyframe
animation rule:Of course, you will need to adjust the values according to the desired offsets/timings.
Alternative: animated
stroke-dashoffset
Probably the easiest approach – slightly jittery. I use
pathLength
to change the computed length for the dash-array to 100.Setting the first dash-array value to 0 will result in a dotted line (so every dot is perfectly circular) when combined with
stroke-linecap: round
.The second value defines the the gap or the total number of dots:
100 (pathlength) / 15 (3 circles à 5 dots) = 0.666.