Fifth of twelve animations.
Loading Animations (13 part series)
Animation
The animation is a Newton's Cradle, final 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 /> </defs> <!-- background --> <rect /> <!-- drawing of the paths traced by the circles, for debugging --> <g> <use /> <use /> <use /> <use /> </g> <!-- circles with different animations --> <ellipse /> <ellipse /> <ellipse /> <ellipse /> <ellipse /> <ellipse /> <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
Inside defs, I defined the paths that the circles follow. They are referenced by their respective IDs.
#left-arcand#right-arc: represent the pendulum motion of circles 1 and 5#left-lineand#right-line: represent a slight impact movement of circles 2 and 4
After the rect, which is the background, I defined use elements to visualize the paths, since elements defined within defs are not rendered. These use elements are removed upon the animation’s completion.
Result
Code
<svg> <defs> <path id="left-arc" d="M68,115 Q38,115 38,85" /> <path id="right-arc" d="M132,115 Q162,115 162,85" /> <path id="left-line" d="M84,115 L83.2,115" /> <path id="right-line" d="M116,115 116.8,115" /> </defs> <rect /> <g fill="none" stroke="lightgray" stroke-width="1"> <use href="#left-arc" /> <use href="#right-arc" /> <use href="#left-line" /> <use href="#right-line" /> </g></svg>Circles and their animations
The final animation duration is 0.7 seconds. In this section, the animations last 10 seconds to make them more noticeable.
Animations that follow the path
Initially, I created the animations for circles 1 and 2. My intention was to mirror these animations for circles 4 and 5. Circle 3 does not have an animation.
Animating circle 1
I used an ellipse element, as it allows setting the radius for the x and y axes separately. The mpath element references path#left-arc, and the animateMotion element defines the animation.
In the animateMotion element, I defined the attributes as follows:
id="circle_left_arc": ID used in thebeginattribute of other animations.begin="0; circle_right_arc.end": the animation starts immediately and also when circle 5’s animation ends.dur="10000ms": duration is 10 seconds for the examples.calcMode="spline": to use Bézier curves in thekeySplinesattribute.rotate="auto": ensures the circle follows the rotation of the path, maintaining correct orientation.fill="freeze": keeps the circle in its final animation position.
Since they work together, I represented the values of the keyTimes, keyPoints, and keySplines attributes in a table:
keyTimes | keyPoints | keySplines |
|---|---|---|
| 0 | 0 | 0.1, 0.7, 1, 1 |
| 0.5 | 1 | 0.7, 0.1, 1, 1 |
| 1 | 0 | - |
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
In other words, in the first half of the animation, the circle moves from right to left following a Bézier curve, starting fast and ending slow. In the second half, it moves back from left to right, starting slow and ending fast.
Circle 1 animated
Code
<svg> <defs> <path /> <path /> <path /> <path /> </defs> <rect /> <g> <use /> <use /> <use /> <use /> </g> <ellipse rx="8" ry="8" fill="black"> <animateMotion id="circle_left_arc" begin="0; circle_right_arc.end" dur="10000ms" keyTimes="0; 0.5; 1" keyPoints="0; 1; 0" calcMode="spline" keySplines="0.1,0.7,1,1; 0.7,0.1,1,1" rotate="auto" fill="freeze" > <mpath href="#left-arc" /> </animateMotion> </ellipse></svg>Animating circle 2
I used an ellipse element. The mpath references path#left-line, and animateMotion defines the animation.
In the animateMotion element, I defined:
id="circle_left_line": ID used in thebeginattribute of other animations.begin="0; circle_right_arc.end": starts immediately and also when circle 5’s animation ends.dur="2000ms": 2-second duration for the examples.rotate="auto": ensures the circle follows the rotation of the path.fill="freeze": maintains the final animation position.
Values represented in a table:
keyTimes | keyPoints |
|---|---|
| 0 | 0 |
| 0.5 | 1 |
| 1 | 0 |
So, in the first half, the circle moves from right to left linearly. In the second half, it moves back from left to right, also linearly.
I also added circle 3, which has no animation.
Circles 1, 2, and 3 animated
Code
<svg> <defs> <path /> <path /> <path /> <path /> </defs> <rect /> <g> <use /> <use /> <use /> <use /> </g> <ellipse /> <ellipse rx="8" ry="8" fill="black"> <animateMotion id="circle_left_line" begin="0; circle_right_arc.end" dur="2000ms" keyTimes="0; 0.5; 1" keyPoints="0; 1; 0" rotate="auto" fill="freeze" > <mpath href="#left-line" /> </animateMotion> </ellipse> <ellipse rx="8" ry="8" cx="100" cy="115" /></svg>Animating circle 4
Animating circle 4 required two ellipse elements:
- One actually animates, it starts hidden and becomes visible when circle 1’s animation ends.
- The other is visible initially and becomes hidden once circle 1’s animation ends.
This approach helps with positioning and animating circle 4, since setting an element's x,y position changes the animateMotion origin point (point 0,0 becomes x,y, not the top-left of the SVG).
The visible-to-hidden ellipse starts with a black fill and turns transparent using a set element when circle 1’s animation ends.
The hidden-to-visible ellipse starts transparent and turns black via set. It begins invisible because it sits at 0,0 before the animation starts.
The mpath references path#right-line, and animateMotion defines the animation.
animateMotion attributes:
id="circle_right_line": ID used in thebeginattributes.begin="circle_left_arc.end": starts when circle 1’s animation ends.dur="2000ms": duration is 2 seconds.rotate="auto": follows the path’s rotation.fill="freeze": keeps the final position.
Values represented in a table:
keyTimes | keyPoints |
|---|---|
| 0 | 0 |
| 0.5 | 1 |
| 1 | 0 |
So, in the first half, the circle moves left to right linearly. In the second half, it moves right to left linearly.
Circles 1, 2, 3, and 4 animated
Code
<svg> <defs> <path /> <path /> <path /> <path /> </defs> <rect /> <g> <use /> <use /> <use /> <use /> </g> <ellipse /> <ellipse /> <ellipse /> <ellipse rx="8" ry="8" fill="transparent"> <animateMotion id="circle_right_line" begin="circle_left_arc.end" dur="2000ms" keyTimes="0; 0.5; 1" keyPoints="0; 1; 0" rotate="auto" fill="freeze" > <mpath href="#right-line" /> </animateMotion> <set attributeName="fill" to="black" begin="circle_left_arc.end" fill="freeze" /> </ellipse> <ellipse rx="8" ry="8" cx="116" cy="115" fill="black"> <set attributeName="fill" to="transparent" begin="circle_left_arc.end" fill="freeze" /> </ellipse></svg>Animating circle 5
The same approach as circle 4 was used for circle 5, with two ellipse elements.
The initially visible ellipse starts black and becomes transparent when circle 1’s animation ends via a set element.
The initially hidden one starts transparent and becomes black via set. It stays at 0,0 before the animation begins.
The mpath references path#right-arc, and animateMotion defines the animation.
Attributes for animateMotion:
id="circle_right_arc": used in thebeginattribute.begin="circle_left_arc.end": starts immediately when circle 1 ends.dur="10000ms": 10-second duration for examples.calcMode="spline": allows use of Bézier curves inkeySplines.rotate="auto": ensures proper path rotation.fill="freeze": retains final animation state.
Values represented in a table:
keyTimes | keyPoints | keySplines |
|---|---|---|
| 0 | 0 | 0.1, 0.7, 1, 1 |
| 0.5 | 1 | 0.7, 0.1, 1, 1 |
| 1 | 0 | - |
So, in the first half, the circle moves left to right with a Bézier curve, starting fast and ending slow. In the second half, it moves right to left, starting slow and ending fast.
All circles animated
Code
<svg> <defs> <path /> <path /> <path /> <path /> </defs> <rect /> <g> <use /> <use /> <use /> <use /> </g> <ellipse /> <ellipse /> <ellipse /> <ellipse /> <ellipse /> <ellipse rx="8" ry="8" fill="transparent"> <animateMotion id="circle_right_arc" begin="circle_left_arc.end" dur="10000ms" keyTimes="0; 0.5; 1" keyPoints="0; 1; 0" calcMode="spline" keySplines="0.1,0.7,1,1; 0.7,0.1,1,1" rotate="auto" fill="freeze" > <mpath href="#right-arc" /> </animateMotion> <set attributeName="fill" to="black" begin="circle_left_arc.end" fill="freeze" /> </ellipse> <ellipse rx="8" ry="8" cx="132" cy="115" fill="black"> <set attributeName="fill" to="transparent" begin="circle_left_arc.end" fill="freeze" /> </ellipse></svg>Stretching animations
Next, I created animations that distort the circles in some way.
Animating rx and ry circles 1 and 5
To convey impact and speed, I created two animations that stretch the circles horizontally and vertically.
The animate elements share the following attributes:
dur="10000ms": 10-second duration for the examples.keyTimes="0; 0.25; 0.5; 0.75; 1": defines when the values are applied to theattributeNameduring the animation.
For the values attribute:
attributeName="rx" | attributeName="ry" | Description |
|---|---|---|
| 8 | 8 | Starts round |
| 7.6 | 8.4 | Stretches vertically and shrinks horizontally to express impact |
| 8 | 8 | Returns to round at the top |
| 8.4 | 7.6 | Stretches horizontally and shrinks vertically to express downward speed |
| 8 | 8 | Returns to round when back at the bottom |
For the begin attribute, the animate elements start when their neighboring animateMotion starts:
begin="circle_left_arc.begin": for circle 1begin="circle_right_arc.begin": for circle 5
All circles animated
Code
<svg> <defs> <path /> <path /> <path /> <path /> </defs> <rect /> <g> <use /> <use /> <use /> <use /> </g> <ellipse> <animateMotion /> <animate attributeName="rx" begin="circle_left_arc.begin" dur="10000ms" keyTimes="0; 0.25; 0.5; 0.75; 1" values="8; 7.6; 8; 8.4; 8" /> <animate attributeName="ry" begin="circle_left_arc.begin" dur="10000ms" keyTimes="0; 0.25; 0.5; 0.75; 1" values="8; 8.4; 8; 7.6; 8" /> </ellipse> <ellipse /> <ellipse /> <ellipse /> <ellipse /> <ellipse> <animateMotion /> <animate attributeName="rx" begin="circle_right_arc.begin" dur="10000ms" keyTimes="0; 0.25; 0.5; 0.75; 1" values="8; 7.6; 8; 8.4; 8" /> <animate attributeName="ry" begin="circle_right_arc.begin" dur="10000ms" keyTimes="0; 0.25; 0.5; 0.75; 1" values="8; 8.4; 8; 7.6; 8" /> <set /> </ellipse> <ellipse /></svg>Conclusion
In this animation, I was able to use syncbase values again in the begin attribute, allowing animation timing to be coordinated reactively, elements on one side respond to the end of animations on the other.
I also explored using the set element for the first time to change attributes based on syncbase values. This opened up new possibilities to manipulate properties in a targeted way, without needing a full animation when a simple state change is enough.
I intend to continue exploring these tools 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="left-arc" d="M68,115 Q38,115 38,85" /> <path id="right-arc" d="M132,115 Q162,115 162,85" /> <path id="left-line" d="M84,115 L83.2,115" /> <path id="right-line" d="M116,115 116.8,115" /> </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_left_arc" begin="0; circle_right_arc.end" dur="700ms" keyTimes="0; 0.5; 1" keyPoints="0; 1; 0" calcMode="spline" keySplines="0.1,0.7,1,1; 0.7,0.1,1,1" rotate="auto" fill="freeze" > <mpath href="#left-arc" /> </animateMotion> <animate attributeName="rx" begin="circle_left_arc.begin" dur="700ms" keyTimes="0; 0.25; 0.5; 0.75; 1" values="8; 7.6; 8; 8.4; 8" /> <animate attributeName="ry" begin="circle_left_arc.begin" dur="700ms" keyTimes="0; 0.25; 0.5; 0.75; 1" values="8; 8.4; 8; 7.6; 8" /> </ellipse> <!-- end: circle 1 --> <!-- start: circle 2 --> <ellipse rx="8" ry="8" fill="black"> <animateMotion id="circle_left_line" begin="0; circle_right_arc.end" dur="150ms" keyTimes="0; 0.5; 1" keyPoints="0; 1; 0" rotate="auto" fill="freeze" > <mpath href="#left-line" /> </animateMotion> </ellipse> <!-- end: circle 2 --> <!-- start: circle 3 --> <ellipse rx="8" ry="8" cx="100" cy="115" /> <!-- end: circle 3 --> <!-- start: circle 4 --> <ellipse rx="8" ry="8" fill="transparent"> <animateMotion id="circle_right_line" begin="circle_left_arc.end" dur="150ms" keyTimes="0; 0.5; 1" keyPoints="0; 1; 0" rotate="auto" fill="freeze" > <mpath href="#right-line" /> </animateMotion> <set attributeName="fill" to="black" begin="circle_left_arc.end" fill="freeze" /> </ellipse> <ellipse rx="8" ry="8" cx="116" cy="115" fill="black"> <set attributeName="fill" to="transparent" begin="circle_left_arc.end" fill="freeze" /> </ellipse> <!-- end: circle 4 --> <!-- start: circle 5 --> <ellipse rx="8" ry="8" fill="transparent"> <animateMotion id="circle_right_arc" begin="circle_left_arc.end" dur="700ms" keyTimes="0; 0.5; 1" keyPoints="0; 1; 0" calcMode="spline" keySplines="0.1,0.7,1,1; 0.7,0.1,1,1" rotate="auto" fill="freeze" > <mpath href="#right-arc" /> </animateMotion> <animate attributeName="rx" begin="circle_right_arc.begin" dur="700ms" keyTimes="0; 0.25; 0.5; 0.75; 1" values="8; 7.6; 8; 8.4; 8" /> <animate attributeName="ry" begin="circle_right_arc.begin" dur="700ms" keyTimes="0; 0.25; 0.5; 0.75; 1" values="8; 8.4; 8; 7.6; 8" /> <set attributeName="fill" to="black" begin="circle_left_arc.end" fill="freeze" /> </ellipse> <ellipse rx="8" ry="8" cx="132" cy="115" fill="black"> <set attributeName="fill" to="transparent" begin="circle_left_arc.end" fill="freeze" /> </ellipse> <!-- end: circle 5 --></svg>