Fourth of twelve animations.
Loading Animations (12 part series)
Animation
Result:
Introduction
I created this animation using SVG, following a structure similar to the previous ones:
<!-- dimensions --><svg> <defs> <!-- paths traced by the circles --> <path /> <path /> <path /> <path /> <path /> </defs> <!-- background --> <rect /> <!-- drawing of the paths traced by the circles, for debugging --> <g> <use /> <use /> <use /> <use /> <use /> </g> <!-- four circles like this --> <ellipse> <!-- animations that move the circle along the path --> <animateMotion> <mpath /> </animateMotion> <animateMotion> <mpath /> </animateMotion> <animateMotion> <mpath /> </animateMotion> <animateMotion> <mpath /> </animateMotion> <animateMotion> <mpath /> </animateMotion> <!-- animation that stretches the circle along the x axis --> <animate /> <!-- animation that stretches the circle along the y axis --> <animate /> </ellipse></svg>How it works
To keep the explanation concise, I've left out some elements and attributes from the following code blocks.
Dimensions
I kept the size the same as the previous animations.
Result
Code
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 200 200" width="200" height="200"> <rect width="199" height="199" x="0.5" y="0.5" fill="white" stroke="lightgray" strokeWidth="1" rx="6" /></svg>Path
The path taken has the shape of a pentagon, along which four circles move. Whenever a vertex is left empty, one of the circles pushes another toward that vertex. The movements follow a clockwise direction.
First, I calculated the position of each vertex using JavaScript.
Result
Code
// dimensionsconst width = 200;const height = 200;const xCenter = width / 2;const yCenter = height / 2;// pentagonconst numberOfSides = 5;// verticesconst radius = 35;const points = [];for (let i = 0; i < numberOfSides; i++) { const angle = ((2 * Math.PI) / numberOfSides) * i; const x = radius * Math.cos(angle) + xCenter; const y = radius * Math.sin(angle) + yCenter; points.push({ x, y });}/*[ { x: 135, y: 100 } { x: 110.81559480312316, y: 133.28697807033038 } { x: 71.68440519687684, y: 120.57248383023656 } { x: 71.68440519687684, y: 79.42751616976344 } { x: 110.81559480312315, y: 66.71302192966962 }]*/Next, I calculated the sides. They extend beyond the vertices, creating space for the circles to move, as if they were gaining momentum or being pushed.
Result
Code
// dimensionsconst width = 200;const height = 200;const xCenter = width / 2;const yCenter = height / 2;// pentagonconst numberOfSides = 5;// verticesconst radius = 35;const points = [];for (let i = 0; i < numberOfSides; i++) { const angle = ((2 * Math.PI) / numberOfSides) * i; const x = radius * Math.cos(angle) + xCenter; const y = radius * Math.sin(angle) + yCenter; points.push({ x, y });}/*[ { x: 135, y: 100 } { x: 110.81559480312316, y: 133.28697807033038 } { x: 71.68440519687684, y: 120.57248383023656 } { x: 71.68440519687684, y: 79.42751616976344 } { x: 110.81559480312315, y: 66.71302192966962 }]*/// sidesconst extension = 0.3;const edges = [];for (let i = 0; i < numberOfSides; i++) { const point1 = points[i]; const point2 = points[i + 1] || points[0]; const dx = point2.x - point1.x; const dy = point2.y - point1.y; const length = Math.hypot(dx, dy); const unitDx = dx / length; const unitDy = dy / length; const edgeExtension = length * extension; const x1 = point1.x - unitDx * edgeExtension; const y1 = point1.y - unitDy * edgeExtension; const x2 = point2.x + unitDx * edgeExtension; const y2 = point2.y + unitDy * edgeExtension; edges.push({ x1, y1, x2, y2 });}/*[ { x1: 142.25532155906305, y1: 90.01390657890089, x2: 103.56027324406011, y2: 143.2730714914295 }, { x1: 122.55495168499706, y1: 137.10132634235853, x2: 59.94504831500294, y2: 116.75813555820842 }, { x1: 71.68440519687684, y1: 132.9159741283785, x2: 71.68440519687684, y2: 67.08402587162149 }, { x1: 59.94504831500295, y1: 83.24186444179158, x2: 122.55495168499704, y2: 62.89867365764148 }, { x1: 103.5602732440601, y1: 56.72692850857051, x2: 142.25532155906305, y2: 109.98609342109911 }]*/// left and right vertices positionconst extensionProportion = extension / (1 + 2 * extension);const leftVertice = 0 + extensionProportion; // 0.18749999999999997const rightVertice = 1 - extensionProportion; // 0.8125With the sides calculated, I defined the paths that the circles follow inside defs. They are referenced by their ids, from motion-path-0 to motion-path-4.
After the rect, which serves as the background, I added five use elements to make the paths visible, since anything defined inside defs isn't rendered by default. The use elements are removed once the animation is completed.
Result
Code
<svg> <defs> <path id="motion-path-0" d="M142.25532155906305,90.01390657890089 L103.56027324406011,143.2730714914295" /> <path id="motion-path-1" d="M122.55495168499706,137.10132634235853 L59.94504831500294,116.75813555820842" /> <path id="motion-path-2" d="M71.68440519687684,132.9159741283785 L71.68440519687684,67.08402587162149" /> <path id="motion-path-3" d="M59.94504831500295,83.24186444179158 L122.55495168499704,62.89867365764148" /> <path id="motion-path-4" d="M103.5602732440601,56.72692850857051 L142.25532155906305,109.98609342109911" /> </defs> <rect /> <g fill="none" stroke="lightgray"> <use href="#motion-path-0" /> <use href="#motion-path-1" /> <use href="#motion-path-2" /> <use href="#motion-path-3" /> <use href="#motion-path-4" /> </g></svg>Circles and their animations
The final animation duration is eight seconds. In this section, the animations last 40 seconds to make them more noticeable.
Animations that follow the path
Initially, I created the animations for the pushing circle and the pushed circle. The idea was to reuse these animations for the other two circles, only changing their start times.
Animating the pushing circle
Starting with the pushing circle, I used an ellipse element, as it allows setting the radius separately for the x and y axes.
I defined five animateMotion elements, each containing an mpath element that references the paths from #motion-path-0 to #motion-path-4. Each animateMotion has the following attributes:
id: ranges fromcircle_1_path_0tocircle_1_path_4begin: each animation starts when the previous one ends, repeating indefinitely#circle_1_path_0,0ms; circle_1_path_4.end:0msensures this is the first animation to run, andcircle_1_path_4.endmakes it run again oncecircle_1_path_4finishes#circle_1_path_1to#circle_1_path_4: each starts when the previous animation ends
The following attributes are the same for all animateMotion elements:
dur="8000ms": the animation lasts 40 seconds in the examples, with eachanimateMotionlasting 1/5 of the total duration, which is eight secondscalcMode="spline": to allow using Bézier curves through thekeySplinesattributerotate="auto": so the circle follows the rotation of the pathkeyTimes,keyPoints, andkeySplines: since they work together, I represented their attribute values in a table:
keyTimes | keyPoints | keySplines |
|---|---|---|
| 0 | 0.18749999999999997 (left vertex) | 0.25, 0.75, 1, 1 |
| 0.2 | 0 | 0.42, 0, 0.58, 1 |
| 0.3 | 1 | 0.42, 0, 0.58, 1 |
| 0.5 | 0.8125 (right vertex) | 0, 0, 1, 1 |
| 1 | 0.8125 (right vertex) | - |
A quick reminder of how they work:
keyTimes: defines the specific moments along the animation timeline when thekeyPointsoccur, ranging from0to1keyPoints: defines where the circle should be along the path, also ranging from0to1keySplines: defines Bézier curves to smooth out the progression betweenkeyPoints
Graphical representation:
- The
xandyaxes represent the values of thekeyTimesandkeyPointsattributes, respectively - At the top and on the right, the dark gray lines represent the intervals where the
keySplinesvalues are applied
In other words, during the first half of the animation, the circle starts at the left vertex, gains momentum, and quickly moves to the other end of the line, stopping at the right vertex. This way, the next animation starts from the same position, keeping the motion continuous.
In the second half, the circle remains stationary. During this interval, the other pair of circles is animated.
Only the pushing circle animated
Code
<svg> <defs> <path /> <path /> <path /> <path /> <path /> </defs> <rect /> <g> <use /> <use /> <use /> <use /> <use /> </g> <ellipse rx="8" ry="8" fill="black"> <animateMotion id="circle_1_path_0" dur="8000ms" calcMode="spline" begin="0ms; circle_1_path_4.end" keyTimes="0; 0.2; 0.3; 0.5; 1" keyPoints="0.18749999999999997; 0; 1; 0.8125; 0.8125" keySplines="0.25, 0.75, 1, 1; 0.42, 0, 0.58, 1; 0.42, 0, 0.58, 1; 0, 0, 1, 1" rotate="auto" > <mpath href="#motion-path-0" /> </animateMotion> <animateMotion id="circle_1_path_1" dur="8000ms" calcMode="spline" begin="circle_1_path_0.end" keyTimes="0; 0.2; 0.3; 0.5; 1" keyPoints="0.18749999999999997; 0; 1; 0.8125; 0.8125" keySplines="0.25, 0.75, 1, 1; 0.42, 0, 0.58, 1; 0.42, 0, 0.58, 1; 0, 0, 1, 1" rotate="auto" > <mpath href="#motion-path-1" /> </animateMotion> <animateMotion id="circle_1_path_2" dur="8000ms" calcMode="spline" begin="circle_1_path_1.end" keyTimes="0; 0.2; 0.3; 0.5; 1" keyPoints="0.18749999999999997; 0; 1; 0.8125; 0.8125" keySplines="0.25, 0.75, 1, 1; 0.42, 0, 0.58, 1; 0.42, 0, 0.58, 1; 0, 0, 1, 1" rotate="auto" > <mpath href="#motion-path-2" /> </animateMotion> <animateMotion id="circle_1_path_3" dur="8000ms" calcMode="spline" begin="circle_1_path_2.end" keyTimes="0; 0.2; 0.3; 0.5; 1" keyPoints="0.18749999999999997; 0; 1; 0.8125; 0.8125" keySplines="0.25, 0.75, 1, 1; 0.42, 0, 0.58, 1; 0.42, 0, 0.58, 1; 0, 0, 1, 1" rotate="auto" > <mpath href="#motion-path-3" /> </animateMotion> <animateMotion id="circle_1_path_4" dur="8000ms" calcMode="spline" begin="circle_1_path_3.end" keyTimes="0; 0.2; 0.3; 0.5; 1" keyPoints="0.18749999999999997; 0; 1; 0.8125; 0.8125" keySplines="0.25, 0.75, 1, 1; 0.42, 0, 0.58, 1; 0.42, 0, 0.58, 1; 0, 0, 1, 1" rotate="auto" > <mpath href="#motion-path-4" /> </animateMotion> </ellipse></svg>Animating the pushed circle
Just like with the pushing circle, I used an ellipse element containing five animateMotion elements. Each animateMotion contains an mpath element, but this time it starts by referencing path#motion-path-1, one path ahead of the pushing circle’s animation.
Each animateMotion has the following attributes:
idandbegin: just like the pushing circle, but usingcircle_2_instead ofcircle_1_dur,calcMode, androtate: the same as the pushing circlekeyTimes,keyPoints, andkeySplines:
keyTimes | keyPoints | keySplines |
|---|---|---|
| 0 | 0.18749999999999997 (left vertex) | 0, 0, 1, 1 |
| 0.26 | 0.18749999999999997 (left vertex) | 0.25, 0.75, 1, 1 |
| 0.36 | 1 | 0.42, 0, 0.58, 1 |
| 0.5 | 0.8125 (right vertex) | 0.42, 0, 0.58, 1 |
| 1 | 0.8125 (right vertex) | - |
Graph representation:
In other words, during the first half of the animation, the circle starts at the left vertex, gets pushed, and quickly moves to the other end of the line, stopping at the right vertex. This way, the next animation starts from the same position, keeping the motion continuous.
In the second half, the circle remains stationary. During this interval, the other pair of circles is animated.
First pair of circles animated
Code
<svg> <defs> <path /> <path /> <path /> <path /> <path /> </defs> <rect /> <g> <use /> <use /> <use /> <use /> <use /> </g> <ellipse> <animateMotion> <mpath /> </animateMotion> <animateMotion> <mpath /> </animateMotion> <animateMotion> <mpath /> </animateMotion> <animateMotion> <mpath /> </animateMotion> <animateMotion> <mpath /> </animateMotion> </ellipse> <ellipse rx="8" ry="8" fill="black"> <animateMotion id="circle_2_path_0" dur="8000ms" calcMode="spline" begin="0ms; circle_2_path_4.end" keyTimes="0; 0.26; 0.36; 0.5; 1" keyPoints="0.18749999999999997; 0.18749999999999997; 1; 0.8125; 0.8125" keySplines="0, 0, 1, 1; 0.25, 0.75, 1, 1; 0.42, 0, 0.58, 1; 0.42, 0, 0.58, 1" rotate="auto" > <mpath href="#motion-path-1" /> </animateMotion> <animateMotion id="circle_2_path_1" dur="8000ms" calcMode="spline" begin="circle_2_path_0.end" keyTimes="0; 0.26; 0.36; 0.5; 1" keyPoints="0.18749999999999997; 0.18749999999999997; 1; 0.8125; 0.8125" keySplines="0, 0, 1, 1; 0.25, 0.75, 1, 1; 0.42, 0, 0.58, 1; 0.42, 0, 0.58, 1" rotate="auto" > <mpath href="#motion-path-2" /> </animateMotion> <animateMotion id="circle_2_path_2" dur="8000ms" calcMode="spline" begin="circle_2_path_1.end" keyTimes="0; 0.26; 0.36; 0.5; 1" keyPoints="0.18749999999999997; 0.18749999999999997; 1; 0.8125; 0.8125" keySplines="0, 0, 1, 1; 0.25, 0.75, 1, 1; 0.42, 0, 0.58, 1; 0.42, 0, 0.58, 1" rotate="auto" > <mpath href="#motion-path-3" /> </animateMotion> <animateMotion id="circle_2_path_3" dur="8000ms" calcMode="spline" begin="circle_2_path_2.end" keyTimes="0; 0.26; 0.36; 0.5; 1" keyPoints="0.18749999999999997; 0.18749999999999997; 1; 0.8125; 0.8125" keySplines="0, 0, 1, 1; 0.25, 0.75, 1, 1; 0.42, 0, 0.58, 1; 0.42, 0, 0.58, 1" rotate="auto" > <mpath href="#motion-path-4" /> </animateMotion> <animateMotion id="circle_2_path_4" dur="8000ms" calcMode="spline" begin="circle_2_path_3.end" keyTimes="0; 0.26; 0.36; 0.5; 1" keyPoints="0.18749999999999997; 0.18749999999999997; 1; 0.8125; 0.8125" keySplines="0, 0, 1, 1; 0.25, 0.75, 1, 1; 0.42, 0, 0.58, 1; 0.42, 0, 0.58, 1" rotate="auto" > <mpath href="#motion-path-0" /> </animateMotion> </ellipse></svg>Stretching animations
Next, I created animations that distort the circles in some way.
Animating rx and ry of the pushing circle
To convey the idea of momentum and speed, I created two animations that, together, stretch the circles horizontally and vertically.
The animate elements share the same attribute values:
repeatCount="indefinite": the animation repeats indefinitelydur="8000ms": the duration is eight seconds in the exampleskeyTimes="0; 0.2; 0.3; 0.5; 1": defines the specific moments along the animation timeline when theattributeNamewill assume the specifiedvalues, ranging from0to1
For the values attribute:
attributeName="rx" | attributeName="ry" | Descrição |
|---|---|---|
| 8 | 8 | Starts round |
| 7.6 | 8.4 | Stretches vertically and shrinks horizontally when gaining momentum |
| 8.4 | 7.6 | Stretches horizontally and shrinks vertically when pushing |
| 8 | 8 | Returns to being round |
| 8 | 8 | Stays round |
Only the pushing circle animated
Code
<svg> <defs> <path /> <path /> <path /> <path /> <path /> </defs> <rect /> <g> <use /> <use /> <use /> <use /> <use /> </g> <ellipse> <animateMotion> <mpath /> </animateMotion> <animateMotion> <mpath /> </animateMotion> <animateMotion> <mpath /> </animateMotion> <animateMotion> <mpath /> </animateMotion> <animateMotion> <mpath /> </animateMotion> <animate attributeName="rx" repeatCount="indefinite" dur="8000ms" keyTimes="0; 0.2; 0.3; 0.5; 1" values="8; 7.6; 8.4; 8; 8" /> <animate attributeName="ry" repeatCount="indefinite" dur="8000ms" keyTimes="0; 0.2; 0.3; 0.5; 1" values="8; 8.4; 7.6; 8; 8" /> </ellipse> <ellipse> <animateMotion> <mpath /> </animateMotion> <animateMotion> <mpath /> </animateMotion> <animateMotion> <mpath /> </animateMotion> <animateMotion> <mpath /> </animateMotion> <animateMotion> <mpath /> </animateMotion> </ellipse></svg>Animating rx and ry of the pushed circle
To convey the idea of impact and speed, I created two animations that, together, stretch the circles horizontally and vertically.
The animate elements share the same attribute values:
repeatCount="indefinite": the animation repeats indefinitelydur="8000ms": the duration is eight seconds in the exampleskeyTimes="0; 0.26; 0.36; 0.5; 1": defines the specific moments along the animation timeline when theattributeNamewill assume the specifiedvalues, ranging from0to1
For the values attribute:
attributeName="rx" | attributeName="ry" | Descrição |
|---|---|---|
| 8 | 8 | Starts round |
| 8 | 8 | Stays round |
| 7.6 | 8.4 | Stretches vertically and shrinks horizontally on impact |
| 8 | 8 | Returns to being round |
| 8 | 8 | Stays round |
First pair of circles animated
Code
<svg> <defs> <path /> <path /> <path /> <path /> <path /> </defs> <rect /> <g> <use /> <use /> <use /> <use /> <use /> </g> <ellipse> <animateMotion> <mpath /> </animateMotion> <animateMotion> <mpath /> </animateMotion> <animateMotion> <mpath /> </animateMotion> <animateMotion> <mpath /> </animateMotion> <animateMotion> <mpath /> </animateMotion> <animate /> <animate /> </ellipse> <ellipse> <animateMotion> <mpath /> </animateMotion> <animateMotion> <mpath /> </animateMotion> <animateMotion> <mpath /> </animateMotion> <animateMotion> <mpath /> </animateMotion> <animateMotion> <mpath /> </animateMotion> <animate attributeName="rx" repeatCount="indefinite" dur="8000ms" keyTimes="0; 0.26; 0.36; 0.5; 1" values="8; 8; 7.6; 8; 8" /> <animate attributeName="ry" repeatCount="indefinite" dur="8000ms" keyTimes="0; 0.26; 0.36; 0.5; 1" values="8; 8; 8.4; 8; 8" /> </ellipse></svg>Animating the second pair of circles
The second pair of circles is basically the same as the first one, with the following differences:
- The
ids of eachanimateMotion: they are similar to the other circles, but start withcircle_3_andcircle_4_ - The
begins of eachellipseanimation: the animations start with a half-time delay,-4000msin the examples - The
pathsof eachmpath: the third circle starts atpath#motion-path-2, and the fourth one atpath#motion-path-3
All circles animated
Code
<svg> <defs> <path /> <path /> <path /> <path /> <path /> </defs> <rect /> <g> <use /> <use /> <use /> <use /> <use /> </g> <ellipse> <animateMotion> <mpath /> </animateMotion> <animateMotion> <mpath /> </animateMotion> <animateMotion> <mpath /> </animateMotion> <animateMotion> <mpath /> </animateMotion> <animateMotion> <mpath /> </animateMotion> <animate /> <animate /> </ellipse> <ellipse> <animateMotion> <mpath /> </animateMotion> <animateMotion> <mpath /> </animateMotion> <animateMotion> <mpath /> </animateMotion> <animateMotion> <mpath /> </animateMotion> <animateMotion> <mpath /> </animateMotion> <animate /> <animate /> </ellipse> <ellipse rx="8" ry="8" fill="black"> <animateMotion id="circle_3_path_0" dur="8000ms" calcMode="spline" begin="-4000ms; circle_3_path_4.end" keyTimes="0; 0.2; 0.3; 0.5; 1" keyPoints="0.18749999999999997; 0; 1; 0.8125; 0.8125" keySplines="0.25, 0.75, 1, 1; 0.42, 0, 0.58, 1; 0.42, 0, 0.58, 1; 0, 0, 1, 1" rotate="auto" > <mpath href="#motion-path-2" /> </animateMotion> <animateMotion id="circle_3_path_1" dur="8000ms" calcMode="spline" begin="circle_3_path_0.end" keyTimes="0; 0.2; 0.3; 0.5; 1" keyPoints="0.18749999999999997; 0; 1; 0.8125; 0.8125" keySplines="0.25, 0.75, 1, 1; 0.42, 0, 0.58, 1; 0.42, 0, 0.58, 1; 0, 0, 1, 1" rotate="auto" > <mpath href="#motion-path-3" /> </animateMotion> <animateMotion id="circle_3_path_2" dur="8000ms" calcMode="spline" begin="circle_3_path_1.end" keyTimes="0; 0.2; 0.3; 0.5; 1" keyPoints="0.18749999999999997; 0; 1; 0.8125; 0.8125" keySplines="0.25, 0.75, 1, 1; 0.42, 0, 0.58, 1; 0.42, 0, 0.58, 1; 0, 0, 1, 1" rotate="auto" > <mpath href="#motion-path-4" /> </animateMotion> <animateMotion id="circle_3_path_3" dur="8000ms" calcMode="spline" begin="circle_3_path_2.end" keyTimes="0; 0.2; 0.3; 0.5; 1" keyPoints="0.18749999999999997; 0; 1; 0.8125; 0.8125" keySplines="0.25, 0.75, 1, 1; 0.42, 0, 0.58, 1; 0.42, 0, 0.58, 1; 0, 0, 1, 1" rotate="auto" > <mpath href="#motion-path-0" /> </animateMotion> <animateMotion id="circle_3_path_4" dur="8000ms" calcMode="spline" begin="circle_3_path_3.end" keyTimes="0; 0.2; 0.3; 0.5; 1" keyPoints="0.18749999999999997; 0; 1; 0.8125; 0.8125" keySplines="0.25, 0.75, 1, 1; 0.42, 0, 0.58, 1; 0.42, 0, 0.58, 1; 0, 0, 1, 1" rotate="auto" > <mpath href="#motion-path-1" /> </animateMotion> <animate attributeName="rx" repeatCount="indefinite" dur="8000ms" begin="-4000ms" keyTimes="0; 0.2; 0.3; 0.5; 1" values="8; 7.6; 8.4; 8; 8" /> <animate attributeName="ry" repeatCount="indefinite" dur="8000ms" begin="-4000ms" keyTimes="0; 0.2; 0.3; 0.5; 1" values="8; 8.4; 7.6; 8; 8" /> </ellipse> <ellipse rx="8" ry="8" fill="black"> <animateMotion id="circle_4_path_0" dur="8000ms" calcMode="spline" begin="-4000ms; circle_4_path_4.end" keyTimes="0; 0.26; 0.36; 0.5; 1" keyPoints="0.18749999999999997; 0.18749999999999997; 1; 0.8125; 0.8125" keySplines="0, 0, 1, 1; 0.25, 0.75, 1, 1; 0.42, 0, 0.58, 1; 0.42, 0, 0.58, 1" rotate="auto" > <mpath href="#motion-path-3" /> </animateMotion> <animateMotion id="circle_4_path_1" dur="8000ms" calcMode="spline" begin="circle_4_path_0.end" keyTimes="0; 0.26; 0.36; 0.5; 1" keyPoints="0.18749999999999997; 0.18749999999999997; 1; 0.8125; 0.8125" keySplines="0, 0, 1, 1; 0.25, 0.75, 1, 1; 0.42, 0, 0.58, 1; 0.42, 0, 0.58, 1" rotate="auto" > <mpath href="#motion-path-4" /> </animateMotion> <animateMotion id="circle_4_path_2" dur="8000ms" calcMode="spline" begin="circle_4_path_1.end" keyTimes="0; 0.26; 0.36; 0.5; 1" keyPoints="0.18749999999999997; 0.18749999999999997; 1; 0.8125; 0.8125" keySplines="0, 0, 1, 1; 0.25, 0.75, 1, 1; 0.42, 0, 0.58, 1; 0.42, 0, 0.58, 1" rotate="auto" > <mpath href="#motion-path-0" /> </animateMotion> <animateMotion id="circle_4_path_3" dur="8000ms" calcMode="spline" begin="circle_4_path_2.end" keyTimes="0; 0.26; 0.36; 0.5; 1" keyPoints="0.18749999999999997; 0.18749999999999997; 1; 0.8125; 0.8125" keySplines="0, 0, 1, 1; 0.25, 0.75, 1, 1; 0.42, 0, 0.58, 1; 0.42, 0, 0.58, 1" rotate="auto" > <mpath href="#motion-path-1" /> </animateMotion> <animateMotion id="circle_4_path_4" dur="8000ms" calcMode="spline" begin="circle_4_path_3.end" keyTimes="0; 0.26; 0.36; 0.5; 1" keyPoints="0.18749999999999997; 0.18749999999999997; 1; 0.8125; 0.8125" keySplines="0, 0, 1, 1; 0.25, 0.75, 1, 1; 0.42, 0, 0.58, 1; 0.42, 0, 0.58, 1" rotate="auto" > <mpath href="#motion-path-2" /> </animateMotion> <animate attributeName="rx" repeatCount="indefinite" dur="8000ms" begin="-4000ms" keyTimes="0; 0.26; 0.36; 0.5; 1" values="8; 8; 7.6; 8; 8" /> <animate attributeName="ry" repeatCount="indefinite" dur="8000ms" begin="-4000ms" keyTimes="0; 0.26; 0.36; 0.5; 1" values="8; 8; 8.4; 8; 8" /> </ellipse></svg>Conclusion
As with the previous animation, I used JavaScript to calculate the values of several attributes, and I also used the graphs that illustrate how the attributes keyTimes, keyPoints, and keySplines work together. This made experimentation much easier throughout the development of the animation.
In addition to applying everything I learned from the previous animations, I used a syncbase value for the first time in the begin attribute. This made managing multiple animateMotion elements used together within the same ellipse element much easier. It’s a tool I plan to use in future animations.
Result
Code
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 200 200" width="200" height="200"> <defs> <path id="motion-path-0" d="M142.25532155906305,90.01390657890089 L103.56027324406011,143.2730714914295" /> <path id="motion-path-1" d="M122.55495168499706,137.10132634235853 L59.94504831500294,116.75813555820842" /> <path id="motion-path-2" d="M71.68440519687684,132.9159741283785 L71.68440519687684,67.08402587162149" /> <path id="motion-path-3" d="M59.94504831500295,83.24186444179158 L122.55495168499704,62.89867365764148" /> <path id="motion-path-4" d="M103.5602732440601,56.72692850857051 L142.25532155906305,109.98609342109911" /> </defs> <rect width="199" height="199" x="0.5" y="0.5" fill="white" stroke="lightgray" stroke-width="1" rx="6" /> <!-- start: circle 1 --> <ellipse rx="8" ry="8" fill="black"> <animateMotion id="circle_1_path_0" dur="1600ms" calcMode="spline" begin="0ms; circle_1_path_4.end" keyTimes="0; 0.2; 0.3; 0.5; 1" keyPoints="0.18749999999999997; 0; 1; 0.8125; 0.8125" keySplines="0.25, 0.75, 1, 1; 0.42, 0, 0.58, 1; 0.42, 0, 0.58, 1; 0, 0, 1, 1" rotate="auto" > <mpath href="#motion-path-0" /> </animateMotion> <animateMotion id="circle_1_path_1" dur="1600ms" calcMode="spline" begin="circle_1_path_0.end" keyTimes="0; 0.2; 0.3; 0.5; 1" keyPoints="0.18749999999999997; 0; 1; 0.8125; 0.8125" keySplines="0.25, 0.75, 1, 1; 0.42, 0, 0.58, 1; 0.42, 0, 0.58, 1; 0, 0, 1, 1" rotate="auto" > <mpath href="#motion-path-1" /> </animateMotion> <animateMotion id="circle_1_path_2" dur="1600ms" calcMode="spline" begin="circle_1_path_1.end" keyTimes="0; 0.2; 0.3; 0.5; 1" keyPoints="0.18749999999999997; 0; 1; 0.8125; 0.8125" keySplines="0.25, 0.75, 1, 1; 0.42, 0, 0.58, 1; 0.42, 0, 0.58, 1; 0, 0, 1, 1" rotate="auto" > <mpath href="#motion-path-2" /> </animateMotion> <animateMotion id="circle_1_path_3" dur="1600ms" calcMode="spline" begin="circle_1_path_2.end" keyTimes="0; 0.2; 0.3; 0.5; 1" keyPoints="0.18749999999999997; 0; 1; 0.8125; 0.8125" keySplines="0.25, 0.75, 1, 1; 0.42, 0, 0.58, 1; 0.42, 0, 0.58, 1; 0, 0, 1, 1" rotate="auto" > <mpath href="#motion-path-3" /> </animateMotion> <animateMotion id="circle_1_path_4" dur="1600ms" calcMode="spline" begin="circle_1_path_3.end" keyTimes="0; 0.2; 0.3; 0.5; 1" keyPoints="0.18749999999999997; 0; 1; 0.8125; 0.8125" keySplines="0.25, 0.75, 1, 1; 0.42, 0, 0.58, 1; 0.42, 0, 0.58, 1; 0, 0, 1, 1" rotate="auto" > <mpath href="#motion-path-4" /> </animateMotion> <animate attributeName="rx" repeatCount="indefinite" dur="1600ms" keyTimes="0; 0.2; 0.3; 0.5; 1" values="8; 7.6; 8.4; 8; 8" /> <animate attributeName="ry" repeatCount="indefinite" dur="1600ms" keyTimes="0; 0.2; 0.3; 0.5; 1" values="8; 8.4; 7.6; 8; 8" /> </ellipse> <!-- end: circle 1 --> <!-- start: circle 2 --> <ellipse rx="8" ry="8" fill="black"> <animateMotion id="circle_2_path_0" dur="1600ms" calcMode="spline" begin="0ms; circle_2_path_4.end" keyTimes="0; 0.26; 0.36; 0.5; 1" keyPoints="0.18749999999999997; 0.18749999999999997; 1; 0.8125; 0.8125" keySplines="0, 0, 1, 1; 0.25, 0.75, 1, 1; 0.42, 0, 0.58, 1; 0.42, 0, 0.58, 1" rotate="auto" > <mpath href="#motion-path-1" /> </animateMotion> <animateMotion id="circle_2_path_1" dur="1600ms" calcMode="spline" begin="circle_2_path_0.end" keyTimes="0; 0.26; 0.36; 0.5; 1" keyPoints="0.18749999999999997; 0.18749999999999997; 1; 0.8125; 0.8125" keySplines="0, 0, 1, 1; 0.25, 0.75, 1, 1; 0.42, 0, 0.58, 1; 0.42, 0, 0.58, 1" rotate="auto" > <mpath href="#motion-path-2" /> </animateMotion> <animateMotion id="circle_2_path_2" dur="1600ms" calcMode="spline" begin="circle_2_path_1.end" keyTimes="0; 0.26; 0.36; 0.5; 1" keyPoints="0.18749999999999997; 0.18749999999999997; 1; 0.8125; 0.8125" keySplines="0, 0, 1, 1; 0.25, 0.75, 1, 1; 0.42, 0, 0.58, 1; 0.42, 0, 0.58, 1" rotate="auto" > <mpath href="#motion-path-3" /> </animateMotion> <animateMotion id="circle_2_path_3" dur="1600ms" calcMode="spline" begin="circle_2_path_2.end" keyTimes="0; 0.26; 0.36; 0.5; 1" keyPoints="0.18749999999999997; 0.18749999999999997; 1; 0.8125; 0.8125" keySplines="0, 0, 1, 1; 0.25, 0.75, 1, 1; 0.42, 0, 0.58, 1; 0.42, 0, 0.58, 1" rotate="auto" > <mpath href="#motion-path-4" /> </animateMotion> <animateMotion id="circle_2_path_4" dur="1600ms" calcMode="spline" begin="circle_2_path_3.end" keyTimes="0; 0.26; 0.36; 0.5; 1" keyPoints="0.18749999999999997; 0.18749999999999997; 1; 0.8125; 0.8125" keySplines="0, 0, 1, 1; 0.25, 0.75, 1, 1; 0.42, 0, 0.58, 1; 0.42, 0, 0.58, 1" rotate="auto" > <mpath href="#motion-path-0" /> </animateMotion> <animate attributeName="rx" repeatCount="indefinite" dur="1600ms" keyTimes="0; 0.26; 0.36; 0.5; 1" values="8; 8; 7.6; 8; 8" /> <animate attributeName="ry" repeatCount="indefinite" dur="1600ms" keyTimes="0; 0.26; 0.36; 0.5; 1" values="8; 8; 8.4; 8; 8" /> </ellipse> <!-- end: circle 2 --> <!-- start: circle 3 --> <ellipse rx="8" ry="8" fill="black"> <animateMotion id="circle_3_path_0" dur="1600ms" calcMode="spline" begin="-800ms; circle_3_path_4.end" keyTimes="0; 0.2; 0.3; 0.5; 1" keyPoints="0.18749999999999997; 0; 1; 0.8125; 0.8125" keySplines="0.25, 0.75, 1, 1; 0.42, 0, 0.58, 1; 0.42, 0, 0.58, 1; 0, 0, 1, 1" rotate="auto" > <mpath href="#motion-path-2" /> </animateMotion> <animateMotion id="circle_3_path_1" dur="1600ms" calcMode="spline" begin="circle_3_path_0.end" keyTimes="0; 0.2; 0.3; 0.5; 1" keyPoints="0.18749999999999997; 0; 1; 0.8125; 0.8125" keySplines="0.25, 0.75, 1, 1; 0.42, 0, 0.58, 1; 0.42, 0, 0.58, 1; 0, 0, 1, 1" rotate="auto" > <mpath href="#motion-path-3" /> </animateMotion> <animateMotion id="circle_3_path_2" dur="1600ms" calcMode="spline" begin="circle_3_path_1.end" keyTimes="0; 0.2; 0.3; 0.5; 1" keyPoints="0.18749999999999997; 0; 1; 0.8125; 0.8125" keySplines="0.25, 0.75, 1, 1; 0.42, 0, 0.58, 1; 0.42, 0, 0.58, 1; 0, 0, 1, 1" rotate="auto" > <mpath href="#motion-path-4" /> </animateMotion> <animateMotion id="circle_3_path_3" dur="1600ms" calcMode="spline" begin="circle_3_path_2.end" keyTimes="0; 0.2; 0.3; 0.5; 1" keyPoints="0.18749999999999997; 0; 1; 0.8125; 0.8125" keySplines="0.25, 0.75, 1, 1; 0.42, 0, 0.58, 1; 0.42, 0, 0.58, 1; 0, 0, 1, 1" rotate="auto" > <mpath href="#motion-path-0" /> </animateMotion> <animateMotion id="circle_3_path_4" dur="1600ms" calcMode="spline" begin="circle_3_path_3.end" keyTimes="0; 0.2; 0.3; 0.5; 1" keyPoints="0.18749999999999997; 0; 1; 0.8125; 0.8125" keySplines="0.25, 0.75, 1, 1; 0.42, 0, 0.58, 1; 0.42, 0, 0.58, 1; 0, 0, 1, 1" rotate="auto" > <mpath href="#motion-path-1" /> </animateMotion> <animate attributeName="rx" repeatCount="indefinite" dur="1600ms" begin="-800ms" keyTimes="0; 0.2; 0.3; 0.5; 1" values="8; 7.6; 8.4; 8; 8" /> <animate attributeName="ry" repeatCount="indefinite" dur="1600ms" begin="-800ms" keyTimes="0; 0.2; 0.3; 0.5; 1" values="8; 8.4; 7.6; 8; 8" /> </ellipse> <!-- end: circle 3 --> <!-- start: circle 4 --> <ellipse rx="8" ry="8" fill="black"> <animateMotion id="circle_4_path_0" dur="1600ms" calcMode="spline" begin="-800ms; circle_4_path_4.end" keyTimes="0; 0.26; 0.36; 0.5; 1" keyPoints="0.18749999999999997; 0.18749999999999997; 1; 0.8125; 0.8125" keySplines="0, 0, 1, 1; 0.25, 0.75, 1, 1; 0.42, 0, 0.58, 1; 0.42, 0, 0.58, 1" rotate="auto" > <mpath href="#motion-path-3" /> </animateMotion> <animateMotion id="circle_4_path_1" dur="1600ms" calcMode="spline" begin="circle_4_path_0.end" keyTimes="0; 0.26; 0.36; 0.5; 1" keyPoints="0.18749999999999997; 0.18749999999999997; 1; 0.8125; 0.8125" keySplines="0, 0, 1, 1; 0.25, 0.75, 1, 1; 0.42, 0, 0.58, 1; 0.42, 0, 0.58, 1" rotate="auto" > <mpath href="#motion-path-4" /> </animateMotion> <animateMotion id="circle_4_path_2" dur="1600ms" calcMode="spline" begin="circle_4_path_1.end" keyTimes="0; 0.26; 0.36; 0.5; 1" keyPoints="0.18749999999999997; 0.18749999999999997; 1; 0.8125; 0.8125" keySplines="0, 0, 1, 1; 0.25, 0.75, 1, 1; 0.42, 0, 0.58, 1; 0.42, 0, 0.58, 1" rotate="auto" > <mpath href="#motion-path-0" /> </animateMotion> <animateMotion id="circle_4_path_3" dur="1600ms" calcMode="spline" begin="circle_4_path_2.end" keyTimes="0; 0.26; 0.36; 0.5; 1" keyPoints="0.18749999999999997; 0.18749999999999997; 1; 0.8125; 0.8125" keySplines="0, 0, 1, 1; 0.25, 0.75, 1, 1; 0.42, 0, 0.58, 1; 0.42, 0, 0.58, 1" rotate="auto" > <mpath href="#motion-path-1" /> </animateMotion> <animateMotion id="circle_4_path_4" dur="1600ms" calcMode="spline" begin="circle_4_path_3.end" keyTimes="0; 0.26; 0.36; 0.5; 1" keyPoints="0.18749999999999997; 0.18749999999999997; 1; 0.8125; 0.8125" keySplines="0, 0, 1, 1; 0.25, 0.75, 1, 1; 0.42, 0, 0.58, 1; 0.42, 0, 0.58, 1" rotate="auto" > <mpath href="#motion-path-2" /> </animateMotion> <animate attributeName="rx" repeatCount="indefinite" dur="1600ms" begin="-800ms" keyTimes="0; 0.26; 0.36; 0.5; 1" values="8; 8; 7.6; 8; 8" /> <animate attributeName="ry" repeatCount="indefinite" dur="1600ms" begin="-800ms" keyTimes="0; 0.26; 0.36; 0.5; 1" values="8; 8; 8.4; 8; 8" /> </ellipse> <!-- end: circle 4 --></svg>