Animated Video Loader Tutorial


Welcome to this video where I suggest you discover how to create an animated loader using JavaScript. To create this module we will use the custom elements API to create a new element that we will be able to reuse.

For this loader we will try to use the element that we see on the Estudio Mobile App UI Kit 1 model.

A personalized item?

The creation of a custom element will allow us to have an element that will be simple to use and will also allow us to use the Shadow DOM. This Shadow DOM will allow us to completely isolate the style of our loader so that it does not overflow on the page, and to avoid that the style of the page overflows on our loader. This ensures better insulation but also better working comfort for independent elements.

class SpinningDots extends HTMLElement {
  constructor () {
    Great()
    const styles = window.getComputedStyle (this)
    const width = this.intFromPx (styles.width, 28)
    const strokeWidth = this.intFromPx (styles.strokeWidth, (4/28) * width, 1)
    const circles = this.intFromPx (this.getAttribute ('dots'), 8)
    const root = this.attachShadow ({mode: 'open'}) // Our shadow DOM
    root.innerHTML = `
$ {this.buildStyles (width, circles, strokeWidth)}     $ {this.buildCircles (width, circles, strokeWidth / 2)}     $ {this.buildTrail (width, strokeWidth)}
`   }   // ... }

Small dots

The first element that makes up our loader is a set of points arranged in a circular manner and which rotate slowly. The easiest way to represent these different elements is to use an SVG.

buildCircles (w, n, r) {
  const circleRadius = (w / 2) - r
  let dom = ``
  for (let i = 0; i <n; i ++) {
    const a = (Math.PI / (n / 2)) * i
    const x = circleRadius * Math.sin (a) + w / 2
    const y = circleRadius * Math.cos (a) + w / 2
    dom + = ``
  }
  return dom + ``
}

For the animation part, we just need to apply a CSS rule to animate a 360 ° rotation over a defined duration.

.circles {
  animation: spin 16s linear infinite;
}
@keyframes spin {
    from {transform: rotate (0deg); }
    to {transform: rotate (360deg); }
}

The trainee

The second element that makes up this loader is a drag that also turns on itself. To achieve this effect, just draw a circle at the right size and play on the CSS properties. stroke-dasharray and stroke-dashoffset in order to partially fill the circle.

buildTrail (w, stroke) {
  return `

`
}
.trail {
  animation: spin2 1.6s cubic-bezier (.5, .15, .5, .85) infinite;
}
.trail circle {
  stroke-dasharray: PERIMETER;
  stroke-dashoffset: PERIMETER + 1 * SECTION;
  animation: trail 1.6s cubic-bezier (.5, .15, .5, .85) infinite;
}
@keyframes spin2 {
    from {transform: rotate (0deg); }
    to {transform: rotate (720deg); }
}
@keyframes trail {
  0% {stroke-dashoffset: PERIMETER + 1 * SECTION; }
  50% {stroke-dashoffset: PERIMETER + 2.5 * SECTION; }
  100% {stroke-dashoffset: PERIMETER + 1 * SECTION; }
}

As for the previous element we will then apply a CSS animation in order to perform a complete rotation of the drag. We will use a bézier curve to make the acceleration effect for this animation.

.trail {
  animation: spin2 1.6s cubic-bezier (.5, .15, .5, .85) infinite;
}
@keyframes spin2 {
    from {transform: rotate (0deg); }
    to {transform: rotate (720deg); }
}

Now, if we analyze the original animation more closely, we notice that with the increase in speed the drag becomes longer. This allows a more fluid animation and respect one of the 12 principles of animation Squash and stretch (an element must be deformed to signify a movement). To manage this distortion effect we will animate the properties stroke-dasharray and stroke-dashoffset at the same time as the rotation.

.trail {
  animation: spin2 1.6s cubic-bezier (.5, .15, .5, .85) infinite;
}
.trail circle {
  stroke-dasharray: PERIMETER;
  stroke-dashoffset: PERIMETER + 1 * SECTION;
  animation: trail 1.6s cubic-bezier (.5, .15, .5, .85) infinite;
}
@keyframes spin2 {
    from {transform: rotate (0deg); }
    to {transform: rotate (720deg); }
}
@keyframes trail {
  0% {stroke-dashoffset: PERIMETER + 1 * SECTION; }
  50% {stroke-dashoffset: PERIMETER + 2.5 * SECTION; }
  100% {stroke-dashoffset: PERIMETER + 1 * SECTION; }
}

To generate this CSS, you must first calculate the perimeter of the circle in order to calculate the dasharray and the dashoffset to apply. This code could be simplified thanks to the use of the property pathLength on SVG but the support is not yet optimal (the property is ignored on version 12- of Safari and on Edge).

I want the code!

If you want to take a look at the final code, or just use the element I published the element on github and npm.