Skip to main content

Loading Animations: December

· 11 min read

Last of the twelve animations.


Loading Animations (13 part series)
  1. Introduction
  2. January
  3. February
  4. March
  5. April
  6. May
  7. June
  8. July
  9. August
  10. September
  11. October
  12. November
  13. December

Animation

It's an animation of a waving flag.

Result:

Introduction

I created this animation in SVG, following a structure similar to the previous ones:

<!-- size definitions --><svg>  <!-- background -->  <rect></rect>  <!-- twelve moving circles -->  <circle>    <!-- animation that moves the circle -->    <animateMotion></animateMotion>  </circle></svg>

How it works

info

I have omitted some elements and attributes in the following blocks to keep the explanation more concise.

Size

I kept the same size used in 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 animation uses twelve paths arranged in 3 rows and 4 columns. I calculated them as follows:

const WIDTH = 200;const HEIGHT = 200;const MARGIN_X = WIDTH * 0.31;const MARGIN_Y = HEIGHT * 0.37;const ROW_COUNT = 3;const COL_COUNT = 4;const RADIUS_X = WIDTH * 0.04;const RADIUS_Y = HEIGHT * 0.04;const ROW_GAP = (HEIGHT - 2 * MARGIN_Y) / (ROW_COUNT - 1);const COL_GAP = (WIDTH - 2 * MARGIN_X) / (COL_COUNT - 1);const arcs = [];for (let row = 0; row < ROW_COUNT; row++) {  const cy = MARGIN_Y + row * ROW_GAP;  for (let col = 0; col < COL_COUNT; col++) {    const cx = MARGIN_X + col * COL_GAP;    const path =      `M ${cx} ${cy - RADIUS_Y} ` +      `A ${RADIUS_X} ${RADIUS_Y} 0 1 1 ${cx} ${cy + RADIUS_Y} ` +      `A ${RADIUS_X} ${RADIUS_Y} 0 1 1 ${cx} ${cy - RADIUS_Y}`;    arcs.push(path);  }}/*[  'M 62 66 A 8 8 0 1 1 62 82 A 8 8 0 1 1 62 66',  'M 87.33333333333333 66 A 8 8 0 1 1 87.33333333333333 82 A 8 8 0 1 1 87.33333333333333 66',  'M 112.66666666666666 66 A 8 8 0 1 1 112.66666666666666 82 A 8 8 0 1 1 112.66666666666666 66',  'M 138 66 A 8 8 0 1 1 138 82 A 8 8 0 1 1 138 66',  'M 62 92 A 8 8 0 1 1 62 108 A 8 8 0 1 1 62 92',  'M 87.33333333333333 92 A 8 8 0 1 1 87.33333333333333 108 A 8 8 0 1 1 87.33333333333333 92',  'M 112.66666666666666 92 A 8 8 0 1 1 112.66666666666666 108 A 8 8 0 1 1 112.66666666666666 92',  'M 138 92 A 8 8 0 1 1 138 108 A 8 8 0 1 1 138 92',  'M 62 118 A 8 8 0 1 1 62 134 A 8 8 0 1 1 62 118',  'M 87.33333333333333 118 A 8 8 0 1 1 87.33333333333333 134 A 8 8 0 1 1 87.33333333333333 118',  'M 112.66666666666666 118 A 8 8 0 1 1 112.66666666666666 134 A 8 8 0 1 1 112.66666666666666 118',  'M 138 118 A 8 8 0 1 1 138 134 A 8 8 0 1 1 138 118',]*/

The center of the arc is defined in lines 19 and 22. It is the initial margin plus the offset based on the current line and column. They were positioned as follows:



Lines 25 and 26 define the first arc (the first half of the circle). They were drawn as follows:



Line 27 defines the second arc (second half of the circle). They were drawn as follows:



Result



Code

<svg>  <rect></rect>  <path    fill="none"    stroke="lightgray"    stroke-width="1"    d="M 62 66 A 8 8 0 1 1 62 82 A 8 8 0 1 1 62 66"  ></path>  <path    fill="none"    stroke="lightgray"    stroke-width="1"    d="M 87.33333333333333 66 A 8 8 0 1 1 87.33333333333333 82 A 8 8 0 1 1 87.33333333333333 66"  ></path>  <path    fill="none"    stroke="lightgray"    stroke-width="1"    d="M 112.66666666666666 66 A 8 8 0 1 1 112.66666666666666 82 A 8 8 0 1 1 112.66666666666666 66"  ></path>  <path    fill="none"    stroke="lightgray"    stroke-width="1"    d="M 138 66 A 8 8 0 1 1 138 82 A 8 8 0 1 1 138 66"  ></path>  <path    fill="none"    stroke="lightgray"    stroke-width="1"    d="M 62 92 A 8 8 0 1 1 62 108 A 8 8 0 1 1 62 92"  ></path>  <path    fill="none"    stroke="lightgray"    stroke-width="1"    d="M 87.33333333333333 92 A 8 8 0 1 1 87.33333333333333 108 A 8 8 0 1 1 87.33333333333333 92"  ></path>  <path    fill="none"    stroke="lightgray"    stroke-width="1"    d="M 112.66666666666666 92 A 8 8 0 1 1 112.66666666666666 108 A 8 8 0 1 1 112.66666666666666 92"  ></path>  <path    fill="none"    stroke="lightgray"    stroke-width="1"    d="M 138 92 A 8 8 0 1 1 138 108 A 8 8 0 1 1 138 92"  ></path>  <path    fill="none"    stroke="lightgray"    stroke-width="1"    d="M 62 118 A 8 8 0 1 1 62 134 A 8 8 0 1 1 62 118"  ></path>  <path    fill="none"    stroke="lightgray"    stroke-width="1"    d="M 87.33333333333333 118 A 8 8 0 1 1 87.33333333333333 134 A 8 8 0 1 1 87.33333333333333 118"  ></path>  <path    fill="none"    stroke="lightgray"    stroke-width="1"    d="M 112.66666666666666 118 A 8 8 0 1 1 112.66666666666666 134 A 8 8 0 1 1 112.66666666666666 118"  ></path>  <path    fill="none"    stroke="lightgray"    stroke-width="1"    d="M 138 118 A 8 8 0 1 1 138 134 A 8 8 0 1 1 138 118"  ></path></svg>

Circles and their animations

Animations that follow the path

Each circle uses an animateMotion element to follow the defined path. The only attribute that varies between circles is begin, which defines a delay for each circle to start its animation.

The circle elements share the following attributes:

  • fill="black": circle fill color
  • r="8": circle radius

The animateMotion elements share the following attributes:

  • dur="1100ms": total animation duration
  • repeatCount="indefinite": the animation repeats indefinitely

I calculated the start delay (begin) of each circle as follows:

const ROW_COUNT = 3;const COL_COUNT = 4;const DURATION = 1_100;const ROW_DELAY = DURATION * 0.08;const COL_DELAY = DURATION * 0.08;const begins = [];for (let row = 0; row < ROW_COUNT; row++) {  const yBegin = row * ROW_DELAY;  for (let col = 0; col < COL_COUNT; col++) {    const xBegin = yBegin + col * COL_DELAY;    const begin = `-${xBegin}ms`;    begins.push(begin);  }}/*[  '-0ms',  '-88ms',  '-176ms',  '-264ms',  '-88ms',  '-176ms',  '-264ms',  '-352ms',  '-176ms',  '-264ms',  '-352ms',  '-440ms',]*/

Lines 11 and 14 define the delays for the rows and columns. Each shift to the right and down adds delay. Since the delays are equal, the circles on the antidiagonal (top right corner to bottom left corner) have the same delay. For example:



Result



Code

<svg>  <rect></rect>  <path></path>  <path></path>  <path></path>  <path></path>  <path></path>  <path></path>  <path></path>  <path></path>  <path></path>  <path></path>  <path></path>  <path></path>  <circle r="8" fill="black">    <animateMotion      dur="1100ms"      path="M 62 66 A 8 8 0 1 1 62 82 A 8 8 0 1 1 62 66 "      begin="-0ms"      repeatCount="indefinite"    ></animateMotion>  </circle>  <circle r="8" fill="black">    <animateMotion      dur="1100ms"      path="M 87.33333333333333 66 A 8 8 0 1 1 87.33333333333333 82 A 8 8 0 1 1 87.33333333333333 66 "      begin="-88ms"      repeatCount="indefinite"    ></animateMotion>  </circle>  <circle r="8" fill="black">    <animateMotion      dur="1100ms"      path="M 112.66666666666666 66 A 8 8 0 1 1 112.66666666666666 82 A 8 8 0 1 1 112.66666666666666 66 "      begin="-176ms"      repeatCount="indefinite"    ></animateMotion>  </circle>  <circle r="8" fill="black">    <animateMotion      dur="1100ms"      path="M 138 66 A 8 8 0 1 1 138 82 A 8 8 0 1 1 138 66 "      begin="-264ms"      repeatCount="indefinite"    ></animateMotion>  </circle>  <circle r="8" fill="black">    <animateMotion      dur="1100ms"      path="M 62 92 A 8 8 0 1 1 62 108 A 8 8 0 1 1 62 92 "      begin="-88ms"      repeatCount="indefinite"    ></animateMotion>  </circle>  <circle r="8" fill="black">    <animateMotion      dur="1100ms"      path="M 87.33333333333333 92 A 8 8 0 1 1 87.33333333333333 108 A 8 8 0 1 1 87.33333333333333 92 "      begin="-176ms"      repeatCount="indefinite"    ></animateMotion>  </circle>  <circle r="8" fill="black">    <animateMotion      dur="1100ms"      path="M 112.66666666666666 92 A 8 8 0 1 1 112.66666666666666 108 A 8 8 0 1 1 112.66666666666666 92 "      begin="-264ms"      repeatCount="indefinite"    ></animateMotion>  </circle>  <circle r="8" fill="black">    <animateMotion      dur="1100ms"      path="M 138 92 A 8 8 0 1 1 138 108 A 8 8 0 1 1 138 92 "      begin="-352ms"      repeatCount="indefinite"    ></animateMotion>  </circle>  <circle r="8" fill="black">    <animateMotion      dur="1100ms"      path="M 62 118 A 8 8 0 1 1 62 134 A 8 8 0 1 1 62 118 "      begin="-176ms"      repeatCount="indefinite"    ></animateMotion>  </circle>  <circle r="8" fill="black">    <animateMotion      dur="1100ms"      path="M 87.33333333333333 118 A 8 8 0 1 1 87.33333333333333 134 A 8 8 0 1 1 87.33333333333333 118 "      begin="-264ms"      repeatCount="indefinite"    ></animateMotion>  </circle>  <circle r="8" fill="black">    <animateMotion      dur="1100ms"      path="M 112.66666666666666 118 A 8 8 0 1 1 112.66666666666666 134 A 8 8 0 1 1 112.66666666666666 118 "      begin="-352ms"      repeatCount="indefinite"    ></animateMotion>  </circle>  <circle r="8" fill="black">    <animateMotion      dur="1100ms"      path="M 138 118 A 8 8 0 1 1 138 134 A 8 8 0 1 1 138 118 "      begin="-440ms"      repeatCount="indefinite"    ></animateMotion>  </circle></svg>

Conclusion

The December animation concludes the series by revisiting many of the concepts explored throughout the year. Using simple paths, organized in a grid, along with small temporal shifts, to suggest organic movement. By distributing these delays across rows and columns, the movement propagates through the grid, creating a continuous and interesting visual rhythm to observe.

With this, I close this cycle of experimentation. More than the final results, the value of this series lies in the process of better understanding SVG, testing ideas, and documenting each step along the way.

Result



Code

<svg  id="svg"  xmlns="http://www.w3.org/2000/svg"  viewBox="0 0 200 200"  width="200"  height="200">  <rect    id="rect"    width="199"    height="199"    rx="6"    x="0.5"    y="0.5"    fill="white"    stroke-width="1"    stroke="lightgray"  ></rect>  <circle r="8" fill="black">    <animateMotion      dur="1100ms"      begin="-0ms"      repeatCount="indefinite"      path="M 62 66 A 8 8 0 1 1 62 82 A 8 8 0 1 1 62 66 "    ></animateMotion>  </circle>  <circle r="8" fill="black">    <animateMotion      dur="1100ms"      begin="-88ms"      repeatCount="indefinite"      path="M 87.33333333333333 66 A 8 8 0 1 1 87.33333333333333 82 A 8 8 0 1 1 87.33333333333333 66 "    ></animateMotion>  </circle>  <circle r="8" fill="black">    <animateMotion      dur="1100ms"      begin="-176ms"      repeatCount="indefinite"      path="M 112.66666666666666 66 A 8 8 0 1 1 112.66666666666666 82 A 8 8 0 1 1 112.66666666666666 66 "    ></animateMotion>  </circle>  <circle r="8" fill="black">    <animateMotion      dur="1100ms"      begin="-264ms"      repeatCount="indefinite"      path="M 138 66 A 8 8 0 1 1 138 82 A 8 8 0 1 1 138 66 "    ></animateMotion>  </circle>  <circle r="8" fill="black">    <animateMotion      dur="1100ms"      begin="-88ms"      repeatCount="indefinite"      path="M 62 92 A 8 8 0 1 1 62 108 A 8 8 0 1 1 62 92 "    ></animateMotion>  </circle>  <circle r="8" fill="black">    <animateMotion      dur="1100ms"      begin="-176ms"      repeatCount="indefinite"      path="M 87.33333333333333 92 A 8 8 0 1 1 87.33333333333333 108 A 8 8 0 1 1 87.33333333333333 92 "    ></animateMotion>  </circle>  <circle r="8" fill="black">    <animateMotion      dur="1100ms"      begin="-264ms"      repeatCount="indefinite"      path="M 112.66666666666666 92 A 8 8 0 1 1 112.66666666666666 108 A 8 8 0 1 1 112.66666666666666 92 "    ></animateMotion>  </circle>  <circle r="8" fill="black">    <animateMotion      dur="1100ms"      begin="-352ms"      repeatCount="indefinite"      path="M 138 92 A 8 8 0 1 1 138 108 A 8 8 0 1 1 138 92 "    ></animateMotion>  </circle>  <circle r="8" fill="black">    <animateMotion      dur="1100ms"      begin="-176ms"      repeatCount="indefinite"      path="M 62 118 A 8 8 0 1 1 62 134 A 8 8 0 1 1 62 118 "    ></animateMotion>  </circle>  <circle r="8" fill="black">    <animateMotion      dur="1100ms"      begin="-264ms"      repeatCount="indefinite"      path="M 87.33333333333333 118 A 8 8 0 1 1 87.33333333333333 134 A 8 8 0 1 1 87.33333333333333 118 "    ></animateMotion>  </circle>  <circle r="8" fill="black">    <animateMotion      dur="1100ms"      begin="-352ms"      repeatCount="indefinite"      path="M 112.66666666666666 118 A 8 8 0 1 1 112.66666666666666 134 A 8 8 0 1 1 112.66666666666666 118 "    ></animateMotion>  </circle>  <circle r="8" fill="black">    <animateMotion      dur="1100ms"      begin="-440ms"      repeatCount="indefinite"      path="M 138 118 A 8 8 0 1 1 138 134 A 8 8 0 1 1 138 118 "    ></animateMotion>  </circle></svg>

Recommended reading