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!