Flipping text with framer-motion



What we are building
Let's get started
First, we need to install the framer-motion library. You can do this by running the following command:
npm install framer-motionNext, we are going to create a page to showcase the component.
Let's make a file called ìndex.js and add the following code:
import "./styles.css";
export default function App() {
return (
<div className="App">
<h1>Rick Astley</h1>
<h2>
He's never gonna <span>give you up</span>
</h2>
</div>
);
}And let's add some styles for the page. Create a file called styles.css and add the following code:
body,
html {
margin: 0;
}
.App {
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
height: 100vh; /* adjust as needed */
text-align: center; /* for centering the text */
font-family: sans-serif;
color: #023047;
}
.App h1 {
font-size: 48px;
font-weight: 300;
line-height: 1rem;
letter-spacing: 0.45rem;
}
.App h2 {
display: inline-block;
padding-right: 10rem;
color: #4a4e69;
font-size: 32px;
font-weight: 300;
}The TextLoop component
Now, let's create a new file called TextLoop.js and add the following code:
import React, { useState, useEffect } from "react";
import { AnimatePresence, motion } from "framer-motion";
const variants = {
initial: {
y: -20,
opacity: 0,
},
fadeIn: {
zIndex: 1,
y: 0,
opacity: 1,
},
exit: {
zIndex: 0,
opacity: 0,
},
};
export const TextLoop = ({ words, className }) => {
const [index, setIndex] = useState(0);
useEffect(() => {
const timeoutId = setTimeout(() => {
let next = index + 1;
if (next === words.length) {
next = 0;
}
setIndex(next);
}, 3 * 1000);
return () => clearTimeout(timeoutId);
}, [index, setIndex]);
return (
<AnimatePresence>
<span className={className}>
<motion.span
style={{ position: "absolute" }}
variants={variants}
key={index}
initial="initial"
animate="fadeIn"
exit="exit"
layout
transition={{
y: { type: "spring", stiffness: 300, damping: 100 },
opacity: { duration: 0.5 },
}}
>
{words[index]}
</motion.span>
</span>
</AnimatePresence>
);
};That's a lot of code so let's break it down:
- We define the
variantsobject that will be used to animate the text. - We create the
TextLoopcomponent that receives an array of words and a className as props. - We added a state variable
indexto keep track of the current word to show. - We use the
useEffecthook to update theindexevery 3 seconds. - We use the
AnimatePresencecomponent to fade out the text when it is removed from the DOM.
Using the TextLoop component
Go back to the ìndex.js file and add the following code:
import "./styles.css";
import { TextLoop } from "./TextLoop";
const Word = ({ color, children }) => (
<span style={{ color: color }} className="word">
{children}
</span>
);
const words = [
<Word color="blue">give you up</Word>,
<Word color="red">let you down</Word>,
<Word color="lime">run around</Word>,
<Word color="purple">desert you</Word>,
];
export default function App() {
return (
<div className="App">
<h1>Rick Astley</h1>
<h2>
He's never gonna <TextLoop className="text-loop" words={words} />
</h2>
</div>
);
}What we did is create a new Word component to handle the styles of the sliding text. Then we created an array of "words" and passed it to the TextLoop component along with a className for styling the container.
Now let's add those styles to the styles.css file:
.text-loop {
margin-left: 8px;
position: relative;
display: inline;
white-space: nowrap;
}
.word {
font-style: italic;
font-weight: bold;
}And that's it! You should now see the text loop animation in action.