Dynamically chain CSS animations

Felix Blaschke
3 min readApr 25, 2018

--

“Macro shot of a chain link.” by Kaley Dykstra on Unsplash

In our previous article we learned a technique to animate HTML elements dynamically with JavaScript using CSS transitions. Now we go one step further and design a chain of animations.

When we use transition we specify several states that gets interpolated by the browser.

Our next challenge is to move a box clockwise: right, down, left and up.

An alternative: This problem can also be solved with @keyframes which has some implications. I solved it in this article. For now we want to stick with JavaScript and CSS transitions to solve this problem dynamically.

The basics

First we define an HTML element with a CSS class box :

<div class="box"></div>

It has the following CSS stylesheet:

.box {
width: 100px;
height: 100px;
background-color: #9D5B75;
transition: transform linear 500ms;
}

Within JavaScript we fetch the element using a CSS selector:

const box = document.body.querySelector("div.box")

We already know that we can animate it with requestAnimationFrame into a second state. What we lack is some kind of “getting informed when the animation to the new state is finished”. Therefor we can listen to an event called transitionend :

box.addEventListener("transitionend", () => console.log('finished'))

For this example I prefer tinting the color of box into red when the animation completes. Have a look:

Chaining states together

Now the idea is to utilize the transitionend event to start a subsequent animation by doing a state change.

Be careful / trap: The first thought might be to put a subsequent state change into the callback function of the transitionend event. This gets confusing really quickly. Don’t do that.

To solve this — without doing a mess — we define a list/array with functions. Every function describes how to get to a specific state.

const states = [
() => box.style.transform = "translate(0px, 0px)",
() => box.style.transform = "translate(100px, 0px)",
() => box.style.transform = "translate(100px, 100px)",
() => box.style.transform = "translate(0px, 100px)"
]

We also introduce a variable that keeps track of the current state:

let currentState = 0

A value of 0 means that we are in a state that relates to states[0] .

Then we need a function, we can call, whenever we want to jump to the next state:

const animateToNextState = () => {
requestAnimationFrame(() => {
states[++currentState % states.length]()
})
}

The expression ++currentState % states.length results in the index of the next state.

What is going on: If we put currentState = 0 into that expression, it increases by one to 1 . states[1] is backed by a function that executes the “move right”. This is exactly what we want to do first.

The modulo operator % makes our computed index repeating right after we reached the last index of states. AcurrentState of 3 increases by one results in 4 . By applying the modulo now, it gets effectively 0 again.

Finally we tell the browser to call our animateToNextState function after any CSS transition completes…

box.addEventListener("transitionend", () => animateToNextState())

… and poke the animation chain once:

animateToNextState()

The result

Now let’s try our implementation…

Challenge completed.

Let’s do something more. In my next article we will focus on creating a pure 100% JavaScript based animation without using CSS Animation or Transition that will run super smooth.

--

--

Responses (1)