Third of twelve animations.
Loading Animations (5 part series)
Animation
Result:
Introduction
I created this animation using SVG, following a structure similar to the previous ones:
<!-- dimensions --><svg> <defs> <!-- path traced by the circles --> <path /> </defs> <!-- background --> <rect /> <!-- drawing of the path traced by the circles, for debugging --> <use /> <!-- three circles like this --> <ellipse> <!-- animation that moves the circle along the path --> <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
Inside defs
, I defined the path
that the circles follow. It is referenced by its id
, motion-path
. The path follows the shape of an infinity symbol, drawn with four arc (A
) commands.
M52,100
: The path starts at the far left.x = 52
is the square width (200
) minus the diameter of both circles (2 * 48 = 96
), divided by both sides ((200 - 96) / 2 = 52
).y = 100
is the vertical center of the square.
- All arcs have the same values for the first four arguments.
rx,ry = 24,24
: same horizontal and vertical radius.x-axis-rotation = 0
: no rotation.large-arc-flag = 0
: the arc should follow the shortest path between start and end points.
A24,24 0 0 1 100,100
:sweep-flag = 1
: sets the arc direction clockwise.x = 100
: horizontal center.y = 100
: vertical center.
A24,24 0 0 0 148,100
:sweep-flag = 0
: sets the arc direction counterclockwise.x = 148
: far right (200 - 52 = 148
).y = 100
: vertical center.
A24,24 0 0 0 100,100
:sweep-flag = 0
: sets the arc direction counterclockwise.x = 100
: horizontal center.y = 100
: vertical center.
A24,24 0 0 1 52,100
:sweep-flag = 1
: sets the arc direction clockwise.x = 52
: far left (0 + 52 = 52
).y = 100
: vertical center.
z
: closes the path.
After defining the rect
(the background), I used a use
element to visualize the path, since elements inside defs
are not rendered. The use
element is removed at the end of the animation.
Result
Code
<svg> <defs> <path id="motion-path" d="M52,100 A24,24 0 0 1 100,100 A24,24 0 0 0 148,100 A24,24 0 0 0 100,100 A24,24 0 0 1 52,100 z" /> </defs> <rect /> <use href="#motion-path" fill="none" stroke="lightgray" stroke-width="1" /></svg>
Circles and their animations
The idea was for the circles to follow the infinity-shaped path. When moving downward, they should accelerate. During acceleration, they should experience a subtle distortion to indicate speed.
The final animation duration is two seconds. In this section, the animations last ten seconds to make them more noticeable.
Animations that follow the path
First, I created the animation that defines the back-and-forth movement of the circles. My goal was to create just one animation, changing the start time for each circle.
Animating one circle
Starting with one of the circles, I used an ellipse
element, as it allows defining separate radii for the x
and y
axes. The mpath
element references path#motion-path
, while animateMotion
defines the animation.
In the animateMotion
element, I set the attributes as follows:
repeatCount="indefinite"
: the animation repeats indefinitely.dur="10000ms"
: duration is ten seconds for demonstration purposes.calcMode="spline"
: enables the use of Bézier curves in thekeySplines
attribute.
Since these attributes work together, I represented the values of keyTimes
, keyPoints
, and keySplines
in a table:
keyTimes | keyPoints | keySplines |
---|---|---|
0 | 0 | 0.6, 0.3, 0.4, 0.7 |
0.5 | 0.5 | 0.6, 0.3, 0.4, 0.7 |
1 | 1 | - |
How they work:
keyTimes
: defines at which specific moments during the animation duration thekeyPoints
occur, ranging from0
to1
.keyPoints
: defines where the circle should be along the path, ranging from0
to1
.keySplines
: defines Bézier curves to smooth the transition betweenkeyPoints
.
Graph representation:
- The
x
andy
axes representkeyTimes
andkeyPoints
values, respectively. - The dark gray lines at the top and right indicate intervals where
keySplines
values apply. - The
0.6,0.3,0.4,0.7
curve starts slow, speeds up in the middle, and slows down at the end.
In the first half of the animation (keyTimes
and keyPoints
from 0
to 0.5
), the circle moves from the far left to the far right following the Bézier curve (0.6, 0.3, 0.4, 0.7
), starting slow, accelerating in the middle, and slowing down at the end. In the second half (keyTimes
and keyPoints
from 0.5
to 1
), the same happens in reverse, moving from the far right back to the far left.
One circle animated
Code
<svg> <defs> <path /> </defs> <rect /> <use /> <ellipse rx="8" ry="8" fill="black"> <animateMotion repeatCount="indefinite" calcMode="spline" dur="10000ms" keyTimes="0; 0.5; 1" keyPoints="0; 0.5; 1" keySplines="0.6,0.3,0.4,0.7; 0.6,0.3,0.4,0.7" > <mpath href="#motion-path" /> </animateMotion> </ellipse></svg>
Animating other circles
As with the first circle, I used two more ellipse
elements. The animateMotion
attributes are the same as the first circle, except the animation starts one-third of the way through:
begin="-3333.3333ms"
andbegin="-6666.6666ms"
: define when the animation starts.
All circles animated
Code
<svg> <defs> <path /> </defs> <rect /> <use /> <ellipse> <animateMotion> <mpath /> </animateMotion> </ellipse> <ellipse> <animateMotion repeatCount="indefinite" calcMode="spline" dur="10000ms" begin="-3333.3333ms" keyTimes="0; 0.5; 1" keyPoints="0; 0.5; 1" keySplines="0.6,0.3,0.4,0.7; 0.6,0.3,0.4,0.7" > <mpath /> </animateMotion> </ellipse> <ellipse> <animateMotion repeatCount="indefinite" calcMode="spline" dur="10000ms" begin="-6666.6666ms" keyTimes="0; 0.5; 1" keyPoints="0; 0.5; 1" keySplines="0.6,0.3,0.4,0.7; 0.6,0.3,0.4,0.7" > <mpath /> </animateMotion> </ellipse></svg>
Stretching Animations
Next, I created animations that distort the circles in some way.
Animating rx
and ry
of one circle
To convey speed, I created two animations that, together, stretch the circles horizontally and vertically.
In animateMotion
, I set rotate="auto"
so the circle follows the path rotation, making the distortion effects rotate automatically as well.
The animate
elements share the same attribute values:
repeatCount="indefinite"
: the animation repeats indefinitely.dur="10000ms"
: duration is ten seconds for demonstration purposes.keyTimes="0; .25; .5; .75; 1"
: defines whenvalues
will be applied during the animation, ranging from0
to1
.
For the values
attribute:
attributeName="rx" | attributeName="ry" | Description |
---|---|---|
8 | 8 | Starts round |
8.4 | 7.6 | Stretches horizontally, compresses vertically to indicate speed when moving down |
8 | 8 | Returns to round shape when moving up |
8.4 | 7.6 | Stretches again when moving down |
8 | 8 | Returns to round shape when moving up |
One circle animated
Code
<svg> <defs> <path /> </defs> <rect /> <use /> <ellipse> <animateMotion repeatCount="indefinite" calcMode="spline" dur="10000ms" keyTimes="0; 0.5; 1" keyPoints="0; 0.5; 1" keySplines="0.6,0.3,0.4,0.7; 0.6,0.3,0.4,0.7" rotate="auto" > <mpath /> </animateMotion> <animate attributeName="rx" repeatCount="indefinite" dur="10000ms" keyTimes="0; .25; .5; .75; 1" values="8; 8.4; 8; 8.4; 8" /> <animate attributeName="ry" repeatCount="indefinite" dur="10000ms" keyTimes="0; .25; .5; .75; 1" values="8; 7.6; 8; 7.6; 8" /> </ellipse> <ellipse> <animateMotion> <mpath /> </animateMotion> </ellipse> <ellipse> <animateMotion> <mpath /> </animateMotion> </ellipse></svg>
All circles animated
Code
<svg> <defs> <path /> </defs> <rect /> <use /> <ellipse> <animateMotion> <mpath /> </animateMotion> <animate /> <animate /> </ellipse> <ellipse> <animateMotion repeatCount="indefinite" calcMode="spline" dur="10000ms" begin="-3333.3333ms" keyTimes="0; 0.5; 1" keyPoints="0; 0.5; 1" keySplines="0.6,0.3,0.4,0.7; 0.6,0.3,0.4,0.7" rotate="auto" > <mpath /> </animateMotion> <animate attributeName="rx" repeatCount="indefinite" dur="10000ms" keyTimes="0; .25; .5; .75; 1" values="8; 8.4; 8; 8.4; 8" begin="-3333.3333ms" /> <animate attributeName="ry" repeatCount="indefinite" dur="10000ms" keyTimes="0; .25; .5; .75; 1" values="8; 7.6; 8; 7.6; 8" begin="-3333.3333ms" /> </ellipse> <ellipse> <animateMotion repeatCount="indefinite" calcMode="spline" dur="10000ms" begin="-6666.6666ms" keyTimes="0; 0.5; 1" keyPoints="0; 0.5; 1" keySplines="0.6,0.3,0.4,0.7; 0.6,0.3,0.4,0.7" rotate="auto" > <mpath /> </animateMotion> <animate attributeName="rx" repeatCount="indefinite" dur="10000ms" keyTimes="0; .25; .5; .75; 1" values="8; 8.4; 8; 8.4; 8" begin="-6666.6666ms" /> <animate attributeName="ry" repeatCount="indefinite" dur="10000ms" keyTimes="0; .25; .5; .75; 1" values="8; 7.6; 8; 7.6; 8" begin="-6666.6666ms" /> </ellipse></svg>
Conclusion
This animation was simpler to develop, mainly because there is no interaction between the circles, and they all share the same animation. Additionally, I didn’t use any new elements or attributes this time but applied everything I learned from previous animations.
During development, I used JavaScript to perform calculations and generate the d
attribute value for the path
element. This allowed for quick testing of different radius
values. For example:
// ellipse
const radius = 24;
// rect
const width = 200;
const height = 200;
const xCenter = width / 2;
const yCenter = height / 2;
const xMargin = (width - 2 * (2 * radius)) / 2;
const yMargin = (height - 2 * radius) / 2;
// path
const motionPath = `
M${xMargin},${yCenter}
A${radius},${radius} 0 0 1 ${xCenter},${yCenter}
A${radius},${radius} 0 0 0 ${width - xMargin},${yCenter}
A${radius},${radius} 0 0 0 ${xCenter},${yCenter}
A${radius},${radius} 0 0 1 ${xMargin},${yCenter}
z
`;
Using JavaScript for calculations like this is a practice I plan to apply 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" d="M52,100 A24,24 0 0 1 100,100 A24,24 0 0 0 148,100 A24,24 0 0 0 100,100 A24,24 0 0 1 52,100 z" /> </defs> <rect width="199" height="199" x="0.5" y="0.5" fill="white" stroke="lightgray" stroke-width="1" rx="6" /> <ellipse rx="8" ry="8" fill="black"> <animateMotion repeatCount="indefinite" calcMode="spline" dur="1400ms" keyTimes="0; 0.5; 1" keyPoints="0; 0.5; 1" keySplines="0.6,0.3,0.4,0.7; 0.6,0.3,0.4,0.7" rotate="auto" > <mpath href="#motion-path" /> </animateMotion> <animate attributeName="rx" repeatCount="indefinite" dur="1400ms" keyTimes="0; .25; .5; .75; 1" values="8; 8.4; 8; 8.4; 8" /> <animate attributeName="ry" repeatCount="indefinite" dur="1400ms" keyTimes="0; .25; .5; .75; 1" values="8; 7.6; 8; 7.6; 8" /> </ellipse> <ellipse rx="8" ry="8" fill="black"> <animateMotion repeatCount="indefinite" calcMode="spline" dur="1400ms" begin="-466.6666ms" keyTimes="0; 0.5; 1" keyPoints="0; 0.5; 1" keySplines="0.6,0.3,0.4,0.7; 0.6,0.3,0.4,0.7" rotate="auto" > <mpath href="#motion-path" /> </animateMotion> <animate attributeName="rx" repeatCount="indefinite" dur="1400ms" keyTimes="0; .25; .5; .75; 1" values="8; 8.4; 8; 8.4; 8" begin="-466.6666ms" /> <animate attributeName="ry" repeatCount="indefinite" dur="1400ms" keyTimes="0; .25; .5; .75; 1" values="8; 7.6; 8; 7.6; 8" begin="-466.6666ms" /> </ellipse> <ellipse rx="8" ry="8" fill="black"> <animateMotion repeatCount="indefinite" calcMode="spline" dur="1400ms" begin="-933.3333ms" keyTimes="0; 0.5; 1" keyPoints="0; 0.5; 1" keySplines="0.6,0.3,0.4,0.7; 0.6,0.3,0.4,0.7" rotate="auto" > <mpath href="#motion-path" /> </animateMotion> <animate attributeName="rx" repeatCount="indefinite" dur="1400ms" keyTimes="0; .25; .5; .75; 1" values="8; 8.4; 8; 8.4; 8" begin="-933.3333ms" /> <animate attributeName="ry" repeatCount="indefinite" dur="1400ms" keyTimes="0; .25; .5; .75; 1" values="8; 7.6; 8; 7.6; 8" begin="-933.3333ms" /> </ellipse></svg>