Getting started with animations
To animate is to transition the user interface from one display to another. Animation enhances user experience, providing feeback to user actions and making screens that haven’t been introduced before easier to understand.
An example can be seen with this React Material UI datepicker. The animations respond to user input and inform the user with transitions that show they are moving back or forward when they change month or select a date.
This post will compare using CSS transitions based on time against using spring phsyics to animate transitions. We will use the example of transitioning a box from left to right (You could imagine this being a month view in a calendar that we slide in and out of view).
Example with no Animation
We will build on this example which doesn’t have any animation. Without animation the transition is jarring and doesn’t show the user where the item is moving from and to. Look at the code in the Babel tab and see that we use transform: translateX rather than setting the left position because left, top, right, bottom are positioned by the CPU and cause a repaint while items using translate are positioned by the GPU and therefore more performant.
Using timed CSS animations
A CSS transition can be added to the properties of an element with the CSS transition property. Per the Mozilla docs for transition, we can pass the duration we want the transition to take and a cubic bezier timing function. In this case I have set the duration to 0.5s and used the default timing function of ease.
A cubic bezier curve is a timing function that starts at position 0 and time 0 and ends at 1,1. It accepts four arguments (x1, y1, x2, y2), which are the two control points to determine the shape of the curve. As you can see above, the ease function accelerates at the start and then decelerates at the middle, causing the animation to ‘ease’ as it finishes. The ease function is just shorthand for cubic-bezier(0.25,0.1,0.25,1). If you want to prove this, re-run this example after changing line 18 to:
transition: '0.5s cubic-bezier(0.25, 0.1, 0.25, 1)'
The cubic bezier site by Lea Verou is a great resource for experimenting with different timing functions. Below I have compared a linear function against the ease function.
Chrome dev tools allow you to experiment with different curves and also control the speed of animations as seen below.
Why you should use spring physics in animations
Look at the cubic bezier animations on the left taken from framerjs, compared with the spring animation on the right. The spring animation has a bounce effect - which is not possible with a cubic bezier function. You could acheive this animation with CSS by using key frames, but you would have to hard code the key frame values and duration of the animation.
React Motion implements a terse API for you to use spring animation which can be used on the web and with React Native. You have the option to specify the start value (in this case, 0) and the spring physics values you want to animate to. React Motion runs in a request animation frame. The Motion component will keep calling your function to render your animated component with a style object that has the calculated values for each frame. In the example below I have destructured the x property and rendered it in the component.
Continuous fluid interfaces
In the excellent talk on the state animation in React, Cheng Lou, the creator, highlighted a quote from a former UIKit engineer at Apple.
Animation APIs parameterized by eg duration and curve are fundamentally opposed to continuous, fluid interactivity.— Andy Matuschak (@andy_matuschak) February 14, 2015
In the talk, Cheng used an example of animating an opening menu to illustrate this point - “For example, if you have a menu deploy animation that takes 500 milliseconds, and half-way the user clicks on something, and you toggle it back to its initial hidden state, why should this way back also be 500 milliseconds? And also, what should the curve be: ease-in, linear? It is not very clear”.
The need for a fluid interface is what made me look into react-motion. We are currently building a calendar at Fergus which requires the ability to respond to drag events and clicks moving the calendar in different directions and by different distances. When we prototyped this with CSS transitions, it was janky and felt slow.
Compare how fluid the spring motion animation is against the CSS animations by clicking the ‘Run animation’ button multiple times in quick succession.
Configuring React Motion
The React Motion spring takes two arguments: stiffness and damping (which defaut to 120 and 17, respectively). Four presets are provided: noWobble, gentle, wobbly, and stiff. By adjusting the stiffness and damping below, you can watch how these factors change the animation.
When to use CSS Animations
As Cheng said in his talk, CSS animations are better for animations that you don’t want to stop or adjust after they are triggered - such as Twitter’s exploding heart animation. This is because CSS animations are more performant. React Motion does incur the cost of having your app re-rendered every animation frame which could be a problem if your application is not performant.