Developers Writing
Published in

Developers Writing

Animating the Unanimatable.

Smooth reordering transitions in React.js

List reordering. Live Example

Identifying The Problem

The solution

  • We know that React just re-rendered, and the DOM nodes have been re-arranged.
  • The browser hasn’t painted yet. Even though the DOM nodes are in their new positions, the on-screen elements haven’t been updated yet.
  • We know where the elements are, on the screen.
  • We know where the elements are about to be re-painted.
Kindly ignore my lack of artistic ability

Order of Operations

DOMRects to the rescue!

blueItem.getBoundingClientRect()
>> {
>> top: 0,
>> bottom: 600,
>> left: 0,
>> right: 500,
>> height: 60,
>> width: 400
>> }
blueItem.getBoundingClientRect()
>> {
>> top: 136,
>> bottom: 464,
>> left: 0,
>> right: 500,
>> height: 60,
>> width: 400
>> }

Δy = finalTop - initialTop = 132 - 0 = 132

.blue-item {
top: -132px
}
.purple-item {
top: 0;
}
.fuscia-item {
top: 132px;
}
  1. React renders our initial state, with the blue item on top. We use getBoundingClientRect to figure out where the items are positioned.
  2. React receives new props: the items have been reversed! Now the blue item is on the bottom.
  3. We use getBoundingClientRect to figure out where the items are now, and calculate the change in positions.
  4. We use requestAnimationFrame to tell the DOM to apply some CSS that undoes this new change; If the element’s new position is 100px lower, we apply CSS to make it 100px higher.

It’s Animation Time

.blue-item {
top: -132px;
}
.blue-item {
transition: top 500ms;
top: 0;
}
No, not that kind of flip.
  • Calculate the First position.
  • Calculate the Last position.
  • Invert the positions
  • Play the animation

A Brief Foray into the DOM

The missing piece: React

  1. Every child needs a unique ‘key’ property. This is what we’ll use to tell them apart.
  2. Every child needs a ref, so that we’ll be able to look up the DOM node and calculate its bounding box.

Getting the First position

Getting the Last position

  • render returns a representation of what it would like the DOM to be,
  • React reconciles this representation with the actual state of the DOM, and applies the differences,
  • The browser notices that something has changed, and calculates the new layout,
  • React’s componentDidUpdate lifecycle method fires,
  • The browser paints the changes to the screen.

Inverting

Playing

Extra Credit

  • We can very easily implement onStart/onFinish callbacks
  • We can do neat things like incrementally offsetting the duration of the transition, so that each animated element takes a little bit longer to finish (this makes animations feel more organic)
  • The full power of Javascript is made available to us; our imagination is the only limit.

Download the Module

Acknowledgements

  • Ryan Florence created a wonderful module, Magic Move, which solves the same problem, albeit in a totally different way.
  • Paul Lewis coined the term FLIP, and the ideas used here come from his fantastic blog post, FLIP your Animations.
  • Sacha Greif and Tom Coleman’s book, Discover Meteor, contains a chapter on animations, and they tackle this problem in a very similar way.

--

--

Developers may not need to blog; but here your words are not wasted.

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store