Moving poetry: Behind the scenes

The “Moving poetry” project (Swedish only) was an exploration of text and technology. This post describes some of the nifty Javascript (and CSS) tricks used to build it.

Web Animations API

The entire project revolves around the “new” Web Animations API, available in modern browsers. As the name suggests, the API makes it possible to animate DOM elements. All properties of an element which can be animated are listed here.

Animations are defined as a KeyframeEffect, which consists of two parts: the keyframes describing the transitions of the animation, and the options controlling the animations behavior.

Below some examples are shown of what effects can be achieved when animating text elements (all but the font size animation can of course be applied to any type of element).

Opacity

To make an element fade in and/or out, the opacity can be animated.

Notice the difference when pressing the cancel or finish button: cancelling an animation will remove any applied effects (returning the element to its starting state) while finishing an animation will set the current playback time to the end of the animation, which combined with the animation option fill: 'forwards' puts the element in its final animated state.

Safari bug: When animating the opacity it seems Safari does some strange things. In below example (Safari only, reproducible at least in version 14.1.2, even though it seems like an old issue) the top two lines fade out even though only the first line has its opacity animated. The third line only stays visible since it has opacity: 1 applied by CSS.

Font size

By animating the font size, text can be made to smoothly disappear or appear:

A small problem, illustrated by the red border in above example, is that the element itself is still rendered. This can be fixed by using Animation.onfinish to apply a CSS class hiding the element.

Translation

The movement of elements can also be animated.

Using the cubic-bezier function to control the timing of the animation allows some really cool patterns to emerge while still using the same simple keyframes. CSS-Tricks has further examples with explanations. To experiment with finding the perfect curve you can use cubic-bezier.com.

When there is a single keyframe specified, the browser will interpret it as the end state and try to infer the starting state. Hence, when chaining animations with more than one keyframe it’s necessary to make sure they are still starting from their current state. This can be done using getComputedStyle to get the current transform of the element. Otherwise the result will look very jumpy, as can be seen in the below example: the “Relative” text element moves along a smooth square (split into two separate animations), while the “Basic” text elements jumps between positions.

Rotation

Similarly, the rotation of elements can be animated. By moving the transform-origin, the element can be rotated around different points.

By combining animations more complex effects can be achieved. In the following example an element containing a child element is rotated, before the child element itself is transformed.

Responsiveness

To make sure animations are responsive, and working nicely on smaller screens as well, there are some tricks that can help:

  • Use relative length units for all the things, including animated translations. Especially if you’re using fluid typography the text animations will scale nicely.
  • CSS functions and variables can be used when specifying the animation keyframes. It’s totally legal to specify something like
    { transform: ['translateX(calc(var(--font-size) * 5))'] }
    
  • If you really need to customize the animations based on for example screen size, media queries can be used in Javascript via matchMedia.

Updated: