I've been learning to use Hooks, and I thought it'd be a good time to get my head around the popular Pose declarative animations library. Animations have always been something I've shied away from – all the imperative code is scary! – but with libraries like Pose for React, it's much easier to see how they can work with a declarative programming model.

Here's some simple code, roughly taken from the Pose documentation. It animates a box from its original size to being full-screen when clicked. Except this code doesn't actually run the animation – it just snaps between full-screen and back, with no animation in between. Can you see what's going wrong with it?

If you spotted it straight away, fair play – you're more eagle-eyed than me. It took actually rewriting the Pose documentation code line-by-line to notice the difference: I'd defined the posed.div inside the component function. Presumably that meant the library couldn't store the state it needed in order to actually run the animations!

Here's a fixed version (also hosted here if you can't see the page below!).

I also prefer the look of the animation with a 'backIn' easing function, which is easy to apply by overriding the default transition Pose applies:

const Box = posed.div({
    regular: { width: '10vw', height: '10vh', flip: true },
    fullscreen: { width: "100vw", height: '100vh', flip: true, transition: { duration: 200, ease: 'backIn' } }

FLIP Animations

In my previous post about parallax with SVG images, I described how it's a good idea to avoid animating any properties other than transform and opacity. But above, it looks like we're animating the width and height properties. What's going on here – will this perform well?

The short answer is yes – the animations above run at a consistent 60fps. Specifying flip: true here enables FLIP animations, which calculates the changes which would be made by animating width, height, top, left etc., and animates them using transform instead. It's a cool technique, and actually not too hard to implement!

In short, the steps are:

  • measuring the First state of the animation,
  • measuring the Last state of the animation,
  • calculating the Inverse of the changes, and
  • Playing the animation with transforms instead – hence, FLIP!

The measuring is done by inspecting the DOM using tools like getBoundingClientRect, and the calculations are simple subtractions and divisions: the end left value minus the initial left value gives the value to plug into transform: translateX(_), for example.

But luckily, we don't need to consider any of that – Pose does all the calculations needed for FLIP animations under the hood. So we can animate width and top to our hearts' content, always remembering to specify flip: true. Neat!