A powerful technique for making animations in React

Emmanuel Meric
partoo
Published in
6 min readSep 15, 2021

At Partoo, the user experience is one of the main focus areas. Rendering the content in an animated way makes the user journey more fluid and enjoyable.

Recently, we implemented the real-time loading of new reviews animation. After searching for multiple ways, none of the CSS transition or key-frame animation features would be helpful to do this correctly.

Instead, we found a clever technique from Chrome engineer Paul Lewis called the FLIP technique and implemented it in React.

Why CSS transition and key-frames are not always sufficient ?

CSS based animations are nice, however, there are a few things that they cannot do :

  • Animate a property to an “auto” value

If we want to animate a property from a fixed value to an “auto” value or vice versa, this is not possible with CSS-based animations. There are a few workarounds for this, but they all have some major drawbacks.

“auto” values can’t be animated with CSS
  • Perform animations out of the main thread

Each CSS property that we change is going to trigger some amount of computation that varies from one property to another. csstriggers.com identifies for each property what part of the browser’s rendering engine they trigger when they change.

For the vast majority of CSS properties, it will at least trigger layout and paint, which happen on the main thread. That means that if you have some heavy Javascript code running along with your animation, it will look junky.

  • Perform complex layout animations

Imagine you have a set of elements arranged in a certain layout. Then, if you make a change that will affect elements’ placement in the layout, like adding or deleting an item, it will move all others.

If you want to animate this kind of behavior, it will be very difficult to do with pure CSS animations, because the final position of each element is highly dependent on many factors : the layout, the number of elements, the element styles, your screen dimensions, etc.

The FLIP technique

To overcome this, Chrome engineer Paul Lewis invented a pattern called FLIP. It stands for First, Last, Invert, Play, which are the four phases of the pattern :

1. First

We take measurements using getBoundingClientRect on the element we want to animate.

const element = document.querySelectorAll('#blue-square')[0];// measure element position
const first = element.getBoundingClientRect();

2. Last

We apply whatever changes to the DOM that will make the element move. We measure the final position of the element with a second call to getBoundingClientRect.

// apply whatever changes that will make the element move
element.className = 'ml-5 mt-5';
// measure its final position
const last = element.getBoundingClientRect();

3. Invert

We compute the delta between the final position and the initial position of the element. Then we apply the opposite of the delta using a transform property to set the element back to its initial position. It can be done by generating CSS dynamically or using Web Animation API.

Apart from this, the DOM remains in its final state.

// compute the delta
const deltaX = last.left - first.left;
const deltaY = last.top - first.top;
// apply a transform to reverse it
element.animate([
{transform: `translate(${-deltaX}px, ${-deltaY}px)`},
]);

4. Play

Finally, we apply a transform: translate(0,0); to the element with a transition on transform property. This will override the previous transform while animating the element to its final position.

// cancel the previous transform with an animated transform to 0,0
element.animate([
{transform: 'translate(0, 0)'},
],{duration: 300, easing: 'linear'});

Here is the final result on a simple example :

It might not look like much, but integrated with multiple elements with layout transitions you can start to see the power of this technique (the code is available at the end) :

A FLIP animation on multiple elements

Making FLIP Animations in React

So we can do nice animations with the FLIP pattern but how to do this in React ? This is tricky to implement because React itself takes care of updating the DOM.

At Partoo, we implemented an out-of-the-box wrapper that can be simply applied to any other components that need to be animated.

Here is how we made it.

React lifecycle methods

What we need basically to perform a FLIP animation is :

  • run some code before mutating the DOM (First)
  • mutate the DOM (Last)
  • run some code after having mutated the DOM (Last, Invert, Play)

React takes care itself of mutating the DOM. But we need to run code just before and after the mutation occurs, where to put this code ?

It turns out that there is a lifecycle method that runs synchronously before any DOM updates, called getSnapshotBeforeUpdate. Also, the well-known componentDidUpdate runs synchronously after DOM updates.

Whatever getSnapshotBeforeUpdate returns can be found in the third argument of componentDidUpdate. This is useful for passing information from one to another.

Also, getSnapshotBeforeUpdate has no equivalent in hooks so we must use a class-based component.

Making the FlipAnimated component

Gathering everything together we can put our FLIP animation code inside these two lifecycle methods and get it working in React :

class FlipAnimated extends React.Component {
constructor(props) {
super(props);
this.ref = React.createRef();
}
getSnapshotBeforeUpdate() {
if (this.ref.current) {
// first
return this.ref.current.getBoundingClientRect();
}
return null;
}

// below `snapshot` is whatever returned `getSnapshotBeforeUpdate`
componentDidUpdate(prevProps, prevState, snaphost) {
if (this.ref.current) {
const first = snapshot;
// last
const last = this.ref.current.getBoundingClientRect();
// invert
const deltaX = last.left - first.left;
const deltaY = last.top - first.top;

this.ref.current.animate([
{ transform: `translate(${-deltaX}px, ${-deltaY}px)` },
// play
{ transform: 'translate(0,0)' },
], {
duration: 300,
easing: 'ease-out',
});
}
}
render() {
return React.cloneElement(
this.props.children,
{ ref: this.ref },
);
}
}

Once this component is done, we can simply use it by wrapping any HTML component to animate it. Whatever will make the wrapped component move, it will move smoothly.

<FlipAnimated>
<div className={`square ${marginLeft ? 'ml-5' : ''}`}/></FlipAnimated>

Conclusion

Here is how with a simple React component you can efficiently animate things that would be simply impossible to animate using CSS transitions.

Also, we made only translations with FLIP animations, but we can also use this technique to animate opacity and scale, using opacity and transform: scale() properties, which are computed on the compositor thread.

Also, if you want to perform spring-based animations, you may want to take a look at react-flip-toolkit which implements this pattern very nicely with a fork of rebound-js for physical computations.

The component FlipAnimated we presented shows the basics of a FLIP animation in React, but you can add a lot of features on your own. Here are a few examples :

  • tune animation duration and easing through props
  • implement an “enter” and “leave” animation with opacity transitions
  • make the animation trigger only on certain circumstances

For example, the FlipAnimated component we use at Partoo implements animation configuration through props as well as a few optimizations.

--

--