Hey, are you keen on discovering new possibilities with Hooke’s Law? Remember the following image that tells us how elasticity works?
Well, I’m not a Physics professor who introduce you to the world of how springs and hooks work, but I can show you how to achieve the same level of flexiblity on a React-based application through React Spring.
React Spring is a spring-physics based animation library that gives all the necessary tools to make simple, yet powerful, animations and interactions via the different interpolations and transitions provided to us.
Before we start coding, let’s take a moment to appreciate Physics.
The term ‘spring’ is heavily involved with the React Spring library.
The following forces are applied to a spring attached to a certain point (A):
In the same fashion, we can describe a spring in React Spring library as something that:
Does NOT have a defined curve or a set duration.
Hence, all the animations are done in terms of time and curves. This is where React Spring comes into play.
Typically, we would have normal @keyframes
animations in CSS that basically deal with time-based animation. Here, due to nature-inspired easing, the animations feel more natural.
First, we’ll make this basic transition to understand how things work:
Step 1: Installation
After you’ve created a new React project, open up your terminal window and run the following command:
npm install react-spring
This should install all the necessary files related to the library.
Step 2: The Toggle Component
Create a new file under the src/ folder of your project called Toggle.jsx.
Start with the usual stuff, like exporting the component and returning the div
container, which has two children: the <h1>
heading, “Hello” and next to the <button>
“Toggle.”
Nothing fancy here; so, let’s add the ‘fancy’.
We’ll be using the useState
Hook in order to handle the state of our toggle. Start by importing it and giving it the isToggled
state variable. The initial value of the state should be set to false
as we don’t want the toggle state to show at first.
const [isToggled, setIsToggled] = useState(false);
Next, to start using React Spring, we need to import the useSpring
hook. This turns the normal value of an element into an animated-value.
Let’s name our animation, fade. Inside the useSpring
hook object, we’ll define all the animations we need. As you can see in the demo above, when the toggle button is clicked, the text changes,
As shown in the documentation, the useSpring
hook takes in various properties like a normal CSS code. However, here we have a React Hook; so, we will pass the color
, transform
, and fontSize
properties (notice the change in syntax compared to CSS) with the isToggled
variable.
If the toggle has not changed its state, then the color
would be #000
; but, when it does change (when the button is pressed), we set it to green
.
The same goes for the other two properties we want to animate:
const fade = useSpring({
color: isToggled ? '#000' : 'green',
transform: isToggled
? 'translate3d(0, 15px, 0)'
: 'translate3d(0, 15px, 0)',
fontSize: isToggled ? '2rem' : '3rem',
});
Okay, but we still haven’t written the <button>
functionality! Let’s write it.
We need to add an onClick
event that passes in the setIsToggled
Hook functionisToggled
variable.
<button onClick={() => setIsToggled(!isToggled)}>Toggle</button>
For the final part, we will use the animated
prop provided by the library (make sure that you import it). We are going to add this prop to whichever element we want to animate. Here, by clicking the “Toggle” button, we want to animate the heading so, we change the tag from <h1>
to <animated.h1>
.
Step 3: Add the Toggle Component
Finally, go back to the App.js file and return the newly created component. You can also add some styling if you like.
function App() {
return <Toggle />
}
As soon as you do this, you can play with your newly created spring animation! Notice that you didn’t have to care about the easing!
How about we move ahead and make this?
Looks exciting, right? It’s somewhat complex, though. Here’s what we need to do:
We’ll write the code inside App.js itself. Begin by importing the library:
import { useSpring, animated } from 'react-spring';
Inside the return()
method, we have a single <animated.div />
that takes in two of React’s mouse synthetic events (onMouseMove
and onMouseLeave
) for the actions we need to perform. These take in the x
and y
coordinates inside the current viewport/container:
return (
<animated.div
onMouseMove={({ clientX: x, clientY: y }) => set({ xys: calcXY(x, y) })}
onMouseLeave={() => set({ xys: [0, 0, 1] })}
style={{ transform: props.xys.interpolate(perspective) }}
/>
);
Here, we pass in the clientX
and clientY
to be calculated by the calcXY()
function.
The calcXY
function is a simple function that takes x
and y
as its arguments and uses DOM’s Window
interface to get the respective width
and height
.
const calcXY = (x, y) => [
-(y - window.innerHeight / 2) / 15,
(x - window.innerWidth / 2) / 15,
1.0,
];
To set the value of xys
as desired, we make a new global constant and use the perspective()
, rotateX()
, rotateY()
, and scale()
properties.
const perspective = (x, y, s) =>
`perspective(500px)
rotateX(${x}deg)
rotateY(${y}deg)
scale(${s})`;
Notice the use of JavaScript’s template literals to dynamically change the corresponding values. We have to use JavaScript’s template literals because it won’t work if we just declare the new perspective
constant, and we need to use it inside the style
attribute of the <animated.div />
tag as follows:
style={{ transform: props.xys.interpolate(perspective) }}
We pass the perspective
inside the interpolate()
function. As per the docs, the interpolate function is:
A function that takes either a function or an object that forms a range. Interpolations can also form chains that allow you to route one calculation into another or reuse them.
Now, it’s time for some more physics stuff. Inside useSpring()
, we will first pass the default xys
value (which simply translates to the X, Y, and Z coordinates in three dimensions). Then, using the config
property, we can manually define how much mass
, tension
, and friction
an element can have!
Exciting right? All of this is possible thanks to React Spring’s Common API. You can view all the examples and an interactive demo on their website.
As for the styling, it can be achieved quite easily with CSS:
.card {
width: 30rem;
height: 30rem;
border-radius: 15px;
background-image: url(https://images.pexels.com/photos/4870974/pexels-photo-4870974.jpeg?auto=compress&cs=tinysrgb&dpr=2&h=750&w=1260);
background-size: cover;
background-position: center center;
box-shadow: 0px 10px 30px -5px rgba(0, 0, 0, 0.3);
transition: box-shadow 0.5s;
}
.card:hover {
box-shadow: 0px 30px 100px -10px rgba(0, 0, 0, 0.4);
}
Here’s our entire spring code:
const calcXY = (x, y) => [
-(y - window.innerHeight / 2) / 15,
(x - window.innerWidth / 2) / 15,
1.0,
];
const perspective = (x, y, s) =>
`perspective(500px) rotateX(${x}deg) rotateY(${y}deg) scale(${s})`;
function App() {
const [props, set] = useSpring(() => ({
xys: [0, 0, 0.5],
config: { mass: 5, tension: 200, friction: 100 },
}));
return (
<animated.div
className='card'
onMouseMove={({ clientX: x, clientY: y }) => set({ xys: calcXY(x, y) })}
onMouseLeave={() => set({ xys: [0, 0, 1] })}
style={{ transform: props.xys.interpolate(perspective) }}
/>
);
}
body { margin: 0; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', sans-serif; -webkit-font-smoothing: antialiased; -moz-osx-font-smoothing: grayscale; } code { font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', monospace; }
I hope this clears your basic understanding of how you can animate your components in React Spring. There are a lot of other possibilities with this library. You can check all the examples out here.
Thanks for reading, I appreciate it! Have a good day.