In this article, we’ll be exploring one of the best React Frameworks for animations, React Spring, and how you can use React Spring to create simple to complex animations that look beautiful.
While React Spring provides both hook-based and component-based APIs, for the article we’ll exclusively look at the hook-based API for all the animations.
For adding the package to your React project, For npm
npm install react-spring d3-ease
For yarn
yarn add react-spring d3-ease
Here we’re also adding the package d3-ease for using different easing functions to our animations without creating our own. This allows us to create smoothe animations with numerous easing functions at our disposal.
useSpring
- A single spring, moves from state A to B.
useSprings
- Multiple springs, mainly for list of elements/components, each moving from state A to B.
usesTransition
- Multiple springs, one spring follows or ‘trails’ behind the other.
useTrail
- For mounting/unmounting animations(for list of components).
useChain
- To chain or combine multiple animations together.
For the article, we’ll look at the most basic hook useSpring.
To use the useSpring
hook in our project, we import the hook and animated from the react-spring library. We also import the d3-ease library, though it can be ignored if you want to use other easing functions.
import { useSpring, animated } from ‘react-spring’
import * as easings from ‘d3-ease’
We import animated, as native elements need to know how to handle the animated props you pass to it. Animate does this by extended these native elements so it can be animated out React (for performance reasons).
Next we define our spring. The basic structure for using the useSpring hook is:
const Animate = useSpring({
from: {
//Initial CSS Properties here
},
to: {
//Final CSS Properties here
}
})
The useSpring
hook can also be declared as a function or use state variables to animate the element.
const Animate = (visible) => useSpring({
opacity: visible ? '1' : '0'
// other properties...
})
On its own, the previous useSpring
hooks would result in very abrupt animations.
This is due to the hook using default configurations. This can be customized by using the configuration by mentioning in the config
section in the hook declaration.
const animation = useSpring({
from: {
//your properties here...
},
to: {
// your properties here...
},
config: {
mass: 1,
tension: 280,
friction: 120,
clamp: true
}
});
There are few pre-defined presets for the config defined in the library
config.default
{ mass: 1, tension: 170, friction: 26 }
config.gentle
{ mass: 1, tension: 120, friction: 14 }
config.wobbly
{ mass: 1, tension: 180, friction: 12 }
config.stiff
{ mass: 1, tension: 210, friction: 20 }
config.slow
{ mass: 1, tension: 280, friction: 60 }
config.molasses
{ mass: 1, tension: 280, friction: 120 }
These presets can be imported from the library
import { useSpring, animated, config } from ‘react-spring’
import * as easings from ‘d3-ease’
const animation = useSpring({
from: {
//your properties here...
},
to: {
// your properties here...
},
config: {
...config.gentle
clamp: true
}
});
In this section, I'll be demonstrating how we can use React Spring to create beautiful Kinetic Typography, like this one.
Firstly, we would have to create the background section and the useSpring
hook to animate the background.
/* CSS Style for the div */
.back {
position: absolute;
top: 0;
left: 0;
height: 100vh;
width: 100vw;
display: flex;
flex-direction: column;
z-index: -1;
height: 0vh;
background: #ffffff;
}
// useSpring hook for div
const BackAnimate = () => useSpring({
from: {
height: "0vh"
},
to: {
height: "50vh"
},
config: {
easing: easings.easeCubicInOut,
duration: 1200
}
});
This would add the animation to the background <div>
which would increase int height to cover 50% of the viewport height. Notice the config settings, we are using easings and duration instead of physics based animations, as this allows us to set the duration, which is not possible with spring based animations.
The hook can be assigned to the <div>
in the following manner:
<animated.div className="back" key={1} style={BackAnimate()}></animated.div>
Notice the use of <animated.div>
instead of normal div.
animated
is used to extend native elements, which allows these elements to bypass React for frame updates and improve performance. It uses the requestAnimationFrame
from the window
API. The API animates the elements in the background at every repaint, instead of using state
variables to animate elements and re-rendering element again at state change, which native React rendering would perform.
This would give us the required background with the animation as shown below
Next we create the text and its styles along with the useSpring
hook to animate the text. Since we have different directions for different texts, we can include direction in the hook
/* CSS Styles for the each of the texts */
.one {
color: #202020;
display: inline-block;
overflow: hidden;
}
.two {
color: #ffffff;
display:inline-block;
overflow: hidden;
}
// useSpring hook for the text
const TextAnimate = (direction) => useSpring({
from: {
transform: `translateY(${direction * 30}vh)`
},
to: {
transform: "translateY(0vh)"
},
delay: 1200,
config: {
easing: easings.easeCubicInOut,
duration: 1200
}
});
The styles allow the texts to be outside of their original positions, and also stay hidden. We then change the position of the texts to reveal them by the useSpring
hook, which also mentions direction as a parameter. This allows the useSpring
hook to be used for both the texts, with just changing the direction for the translation during the animation using template strings
. The code combined with the background would result in the below animation
The animation could be taken further by creating individual reveal animations for each word by a loop and modifying the useSpring
hook in a manner as shown below
// Modified useSpring hook
const TextAnimate = (direction, index) => useSpring({
from: {
transform: `translateY(${direction * 30}vh)`
},
to: {
transform: "translateY(0vh)" },
delay: 1200 + index * 300,
config: {
easing: easings.easeCubicInOut,
duration: 1200
}
});
// Text splitting for individual animations
{"Lorem ipsum dolor sit amet,"
.split(" ")
.map((item, index) => (
<animated.span className="one" key={index} style={TextAnimate(1, index)}>
{item}
</animated.span>
))}
The split()
function splits the string into characters using the delimiter passed as the parameter, which in this case is " "
character (whitespace character). Which would create an array of words, over which we iterate to create individual elements with their own animations, with the offset passed as the parameter in each iteration to maintain an order of reveal. We use the
unicode character to mention a whitespace as any whitespaces mention as a JavaScript literal is discarded.
Giving you the final result as shown