Eighth of twelve animations.
Loading Animations (13 part series)
Animation
The animation consists of circles traversing a Lissajous curve.
Introduction
I created this animation with SVG. The structure follows the pattern of the previous ones:
<!-- size definitions --><svg> <defs> <!-- path that the circles follow --> <path /> </defs> <!-- background --> <rect /> <!-- drawing the path that the circles follow, for debug --> <use /> <!-- eight circles like this --> <animateMotion> <mpath></mpath> </animateMotion></svg>How it works
I omitted some elements and attributes in the blocks below to keep the explanation concise.
Size
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" stroke-width="1" rx="6" /></svg>Path
To create the path, I used JavaScript to dynamically generate the Lissajous curve:
// dimensionsconst WIDTH = 200;const HEIGHT = 200;const X_CENTER = WIDTH * (1 / 2);const Y_CENTER = HEIGHT * (1 / 2);const X_MARGIN = 25; // distance between the curve and the horizontal edgesconst Y_MARGIN = 75; // distance between the curve and the vertical edgesconst X_RADIUS = (WIDTH - 2 * X_MARGIN) * (1 / 2);const Y_RADIUS = (HEIGHT - 2 * Y_MARGIN) * (1 / 2);// curveconst TAU = 2 * Math.PI;const POINTS_AMOUNT = 100; // higher number of points makes the curve smootherconst A_FREQUENCY = 1;const B_FREQUENCY = 3;const OFFSET = -1 * (1 / 2) * Math.PI; // offset to start the curve on the leftlet d = "";for (let i = 0; i < POINTS_AMOUNT; i++) { const pointIndex = i / POINTS_AMOUNT; const at = A_FREQUENCY * TAU * pointIndex + OFFSET; const bt = B_FREQUENCY * TAU * pointIndex + OFFSET; const x = X_CENTER + X_RADIUS * Math.sin(at); const y = Y_CENTER + Y_RADIUS * Math.cos(bt); if (i === 0) { d += `M${x},${y} `; continue; } d += `L${x},${y} `;}d += `z`;// M25,100 L25.14799536787963,104.68453286464312 ... L25.14799536787963,95.31546713535694 zResult
Code
<svg> <def> <path id="path" d=" M25,100 L25.14799536787963,104.68453286464312 ... L25,99.99999999999994 z " /> </def> <rect /> <use href="#path" stroke="lightgray" fill="none" /></svg>Circles and their animations
With the path ready, the next step was to create the circles and define how they would move along it.
Animations that follow the path
The idea is simple: each circle just follows the path that was defined. When moving from one end to the other, an ease-in-out Bézier curve is applied, it starts slower, accelerates in the middle, and decelerates at the end.
The ellipse elements share the following attributes:
fill="black": fill color of the ellipserx="8": horizontal radius of the ellipsery="8": vertical radius of the ellipse
The animateMotion elements share the following attributes:
dur="20000ms": total duration of the animation, 20 secondsbegin="<delay>": delay before the animation starts, each circle has an equally spaced delayrepeatCount="indefinite": the animation repeats indefinitelycalcMode="spline": uses a Bézier curve for interpolationkeyPoints="0; 0.5; 1": key points of the animationkeyTimes="0; 0.5; 1": times corresponding to the key pointskeySplines="0.5, 0.4, 0.5, 0.6; 0.5, 0.4, 0.5, 0.6": Bézier curves for interpolation
keyTimes | keyPoints | keySplines |
|---|---|---|
| 0 | 0 | 0.5, 0.4, 0.5, 0.6 |
| 0.5 | 0.5 | 0.5, 0.4, 0.5, 0.6 |
| 1 | 1 | - |
The mpath elements share an attribute:
href="#path": reference to the path that the animation should follow
Result
Code
<svg> <def> <path /> </def> <rect /> <use /> <!-- start: circle 1 --> <ellipse fill="black" rx="8" ry="8"> <animateMotion dur="20000ms" begin="-20000ms" repeatCount="indefinite" calcMode="spline" keyPoints="0; 0.5; 1" keyTimes="0; 0.5; 1" keySplines="0.5, 0.4, 0.5, 0.6; 0.5, 0.4, 0.5, 0.6" > <mpath href="#path" /> </animateMotion> <!-- end: circle 1 --> </ellipse> <!-- start: circle 2 --> <ellipse fill="black" rx="8" ry="8"> <animateMotion dur="20000ms" begin="-17500ms" repeatCount="indefinite" calcMode="spline" keyPoints="0; 0.5; 1" keyTimes="0; 0.5; 1" keySplines="0.5, 0.4, 0.5, 0.6; 0.5, 0.4, 0.5, 0.6" > <mpath href="#path" /> </animateMotion> <!-- end: circle 2 --> </ellipse> <!-- start: circle 3 --> <ellipse fill="black" rx="8" ry="8"> <animateMotion dur="20000ms" begin="-15000ms" repeatCount="indefinite" calcMode="spline" keyPoints="0; 0.5; 1" keyTimes="0; 0.5; 1" keySplines="0.5, 0.4, 0.5, 0.6; 0.5, 0.4, 0.5, 0.6" > <mpath href="#path" /> </animateMotion> <!-- end: circle 3 --> </ellipse> <!-- start: circle 4 --> <ellipse fill="black" rx="8" ry="8"> <animateMotion dur="20000ms" begin="-12500ms" repeatCount="indefinite" calcMode="spline" keyPoints="0; 0.5; 1" keyTimes="0; 0.5; 1" keySplines="0.5, 0.4, 0.5, 0.6; 0.5, 0.4, 0.5, 0.6" > <mpath href="#path" /> </animateMotion> <!-- end: circle 4 --> </ellipse> <!-- start: circle 5 --> <ellipse fill="black" rx="8" ry="8"> <animateMotion dur="20000ms" begin="-10000ms" repeatCount="indefinite" calcMode="spline" keyPoints="0; 0.5; 1" keyTimes="0; 0.5; 1" keySplines="0.5, 0.4, 0.5, 0.6; 0.5, 0.4, 0.5, 0.6" > <mpath href="#path" /> </animateMotion> <!-- end: circle 5 --> </ellipse> <!-- start: circle 6 --> <ellipse fill="black" rx="8" ry="8"> <animateMotion dur="20000ms" begin="-7500ms" repeatCount="indefinite" calcMode="spline" keyPoints="0; 0.5; 1" keyTimes="0; 0.5; 1" keySplines="0.5, 0.4, 0.5, 0.6; 0.5, 0.4, 0.5, 0.6" > <mpath href="#path" /> </animateMotion> <!-- end: circle 6 --> </ellipse> <!-- start: circle 7 --> <ellipse fill="black" rx="8" ry="8"> <animateMotion dur="20000ms" begin="-5000ms" repeatCount="indefinite" calcMode="spline" keyPoints="0; 0.5; 1" keyTimes="0; 0.5; 1" keySplines="0.5, 0.4, 0.5, 0.6; 0.5, 0.4, 0.5, 0.6" > <mpath href="#path" /> </animateMotion> <!-- end: circle 7 --> </ellipse> <!-- start: circle 8 --> <ellipse fill="black" rx="8" ry="8"> <animateMotion dur="20000ms" begin="-2500ms" repeatCount="indefinite" calcMode="spline" keyPoints="0; 0.5; 1" keyTimes="0; 0.5; 1" keySplines="0.5, 0.4, 0.5, 0.6; 0.5, 0.4, 0.5, 0.6" > <mpath href="#path" /> </animateMotion> <!-- end: circle 8 --> </ellipse></svg>Conclusion
The eighth animation was a great experience. Working with Lissajous curves brought a unique visual aspect. Additionally, the application of the Bézier curve in the animation of the circles was essential to ensure that they did not overlap, allowing each element to have its own visual space. The use of JavaScript gave me the freedom to experiment dynamically with the number of points on the curve and their positioning, enabling fine-tuning and real-time testing.
Result
Code
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 200 200" width="200" height="200"> <def> <path id="path" d=" M25,100 L25.14799536787963,104.68453286464312 L25.591397401414156,109.20311381711696 L26.328456195348352,113.39566987447492 L27.356262915352673,117.11367764821722 L28.67076127786349,120.22542485937369 L30.266763558381143,122.62067631165048 L32.137971065048546,124.21457902821578 L34.27699899671023,124.9506682107068 L36.67540558734887,124.80286753286194 L39.32372542187894,123.77641290737884 L42.21150679181581,121.9076670010966 L45.32735294339413,119.26283106939474 L48.658967055348356,115.93559974371725 L52.19320076884828,112.04384185254288 L55.916106078064516,107.72542485937369 L59.81299037657526,103.1333308391076 L63.868474442371365,98.43023701176716 L68.06655313261956,93.78275282087864 L72.39065854864916,89.35551771087319 L76.82372542187895,85.30536869268818 L81.34825846263588,81.77578431446472 L85.94640140607065,78.89180186244963 L90.60000748267719,76.75558785279371 L95.29071103530148,75.44281873178278 L100,75 L104.70928896469852,75.44281873178278 L109.39999251732283,76.75558785279372 L114.05359859392937,78.89180186244963 L118.6517415373641,81.7757843144647 L123.17627457812105,85.30536869268818 L127.60934145135084,89.35551771087317 L131.93344686738047,93.78275282087864 L136.13152555762866,98.43023701176718 L140.18700962342476,103.1333308391076 L144.0838939219355,107.72542485937367 L147.80679923115173,112.04384185254287 L151.34103294465166,115.93559974371723 L154.67264705660585,119.26283106939474 L157.78849320818418,121.9076670010966 L160.67627457812105,123.77641290737884 L163.32459441265112,124.80286753286194 L165.72300100328977,124.9506682107068 L167.86202893495147,124.21457902821578 L169.73323644161886,122.6206763116505 L171.3292387221365,120.22542485937369 L172.64373708464734,117.1136776482172 L173.67154380465166,113.39566987447493 L174.40860259858584,109.20311381711694 L174.85200463212038,104.68453286464315 L175,100.00000000000001 L174.85200463212038,95.31546713535687 L174.40860259858584,90.79688618288307 L173.67154380465166,86.60433012552508 L172.64373708464734,82.88632235178277 L171.3292387221365,79.77457514062633 L169.73323644161883,77.3793236883495 L167.86202893495147,75.78542097178422 L165.72300100328977,75.0493317892932 L163.32459441265115,75.19713246713805 L160.67627457812105,76.22358709262116 L157.7884932081842,78.0923329989034 L154.67264705660585,80.73716893060526 L151.34103294465166,84.06440025628275 L147.80679923115173,87.95615814745713 L144.0838939219355,92.2745751406263 L140.18700962342473,96.8666691608924 L136.13152555762863,101.56976298823285 L131.9334468673804,106.21724717912137 L127.60934145135089,110.64448228912678 L123.17627457812107,114.69463130731182 L118.65174153736415,118.22421568553526 L114.05359859392934,121.10819813755037 L109.39999251732284,123.24441214720628 L104.70928896469849,124.55718126821722 L100.00000000000001,125 L95.29071103530153,124.55718126821722 L90.60000748267717,123.24441214720629 L85.94640140607068,121.10819813755037 L81.34825846263587,118.22421568553528 L76.82372542187895,114.69463130731184 L72.39065854864913,110.64448228912681 L68.06655313261962,106.21724717912139 L63.86847444237137,101.56976298823288 L59.81299037657529,96.86666916089243 L55.91610607806452,92.27457514062637 L52.1932007688483,87.95615814745712 L48.65896705534835,84.06440025628278 L45.32735294339415,80.7371689306053 L42.2115067918158,78.0923329989034 L39.32372542187895,76.22358709262116 L36.675405587348855,75.19713246713806 L34.27699899671023,75.0493317892932 L32.13797106504852,75.78542097178422 L30.26676355838117,77.37932368834947 L28.67076127786349,79.7745751406263 L27.356262915352673,82.88632235178282 L26.328456195348352,86.60433012552502 L25.59139740141417,90.79688618288304 L25.14799536787963,95.31546713535694 L25,99.99999999999994 z " /> </def> <rect width="199" height="199" x="0.5" y="0.5" fill="white" stroke="lightgray" stroke-width="1" rx="6" /> <ellipse fill="black" rx="8" ry="8"> <animateMotion dur="20000ms" begin="-20000ms" repeatCount="indefinite" calcMode="spline" keyPoints="0; 0.5; 1" keyTimes="0; 0.5; 1" keySplines="0.5, 0.4, 0.5, 0.6; 0.5, 0.4, 0.5, 0.6" > <mpath href="#path" /> </animateMotion> </ellipse> <ellipse fill="black" rx="8" ry="8"> <animateMotion dur="20000ms" begin="-17500ms" repeatCount="indefinite" calcMode="spline" keyPoints="0; 0.5; 1" keyTimes="0; 0.5; 1" keySplines="0.5, 0.4, 0.5, 0.6; 0.5, 0.4, 0.5, 0.6" > <mpath href="#path" /> </animateMotion> </ellipse> <ellipse fill="black" rx="8" ry="8"> <animateMotion dur="20000ms" begin="-15000ms" repeatCount="indefinite" calcMode="spline" keyPoints="0; 0.5; 1" keyTimes="0; 0.5; 1" keySplines="0.5, 0.4, 0.5, 0.6; 0.5, 0.4, 0.5, 0.6" > <mpath href="#path" /> </animateMotion> </ellipse> <ellipse fill="black" rx="8" ry="8"> <animateMotion dur="20000ms" begin="-12500ms" repeatCount="indefinite" calcMode="spline" keyPoints="0; 0.5; 1" keyTimes="0; 0.5; 1" keySplines="0.5, 0.4, 0.5, 0.6; 0.5, 0.4, 0.5, 0.6" > <mpath href="#path" /> </animateMotion> </ellipse> <ellipse fill="black" rx="8" ry="8"> <animateMotion dur="20000ms" begin="-10000ms" repeatCount="indefinite" calcMode="spline" keyPoints="0; 0.5; 1" keyTimes="0; 0.5; 1" keySplines="0.5, 0.4, 0.5, 0.6; 0.5, 0.4, 0.5, 0.6" > <mpath href="#path" /> </animateMotion> </ellipse> <ellipse fill="black" rx="8" ry="8"> <animateMotion dur="20000ms" begin="-7500ms" repeatCount="indefinite" calcMode="spline" keyPoints="0; 0.5; 1" keyTimes="0; 0.5; 1" keySplines="0.5, 0.4, 0.5, 0.6; 0.5, 0.4, 0.5, 0.6" > <mpath href="#path" /> </animateMotion> </ellipse> <ellipse fill="black" rx="8" ry="8"> <animateMotion dur="20000ms" begin="-5000ms" repeatCount="indefinite" calcMode="spline" keyPoints="0; 0.5; 1" keyTimes="0; 0.5; 1" keySplines="0.5, 0.4, 0.5, 0.6; 0.5, 0.4, 0.5, 0.6" > <mpath href="#path" /> </animateMotion> </ellipse> <ellipse fill="black" rx="8" ry="8"> <animateMotion dur="20000ms" begin="-2500ms" repeatCount="indefinite" calcMode="spline" keyPoints="0; 0.5; 1" keyTimes="0; 0.5; 1" keySplines="0.5, 0.4, 0.5, 0.6; 0.5, 0.4, 0.5, 0.6" > <mpath href="#path" /> </animateMotion> </ellipse></svg>