I'm not sure what to call it other than rotating word loop, but basically I mean the animation where a word disappears upwards or downwards and another word appears. For an example you can check the Auth0 website, which is what initially inspired me to want to create this.
I'm really bad at animations, but I'm using Chakra UI, which comes with Framer motion, so I want to use that to create this animation. I have managed to get something working, but it only seems to work properly when the parent container's display is set to inline, which means there isn't any space between it and the preceding word (unless I add margin of course). It also feels like there should be a better way of doing it than adding position: absolute, but everything I've tried hasn't worked. Here is the code:
import {
Box,
Center,
chakra,
ChakraProvider,
Heading,
Text
} from "@chakra-ui/react";
import { AnimatePresence, motion } from "framer-motion";
import * as React from "react";
import { createRoot } from "react-dom/client";
const MotionSpan = motion(chakra.span);
interface TextLoopProps {
texts: string[];
}
const TextLoop: React.FC<TextLoopProps> = ({ texts }) => {
const [index, setIndex] = React.useState<number>(0);
React.useEffect(() => {
setTimeout(() => {
let next = index + 1;
setIndex(next % texts.length);
}, 3 * 1000);
}, [index, setIndex, texts]);
return (
<Box display="inline" position="relative" w="100%">
<AnimatePresence initial={false}>
<MotionSpan
position="absolute"
key={index}
layout
variants={{
enter: {
translateY: 20,
opacity: 0,
height: 0
},
center: {
zIndex: 1,
translateY: 0,
opacity: 1,
height: "auto"
},
exit: {
zIndex: 0,
translateY: -20,
opacity: 0,
height: 0
}
}}
initial="enter"
animate="center"
exit="exit"
transition={{
translateY: { type: "spring", stiffness: 300, damping: 200 },
opacity: { duration: 0.5 }
}}
>
{texts[index]}
</MotionSpan>
</AnimatePresence>
</Box>
);
};
function App() {
return (
<Center flexDirection="column" h="100vh" w="100vw">
<Box>
<Heading>
<Text as="span">Secure access</Text>
<br />
<Text as="span">for </Text>
<TextLoop texts={["mice", "sheep", "cows"]} />
</Heading>
<Heading>
<Text as="span">But not</Text>{" "}
<TextLoop texts={["cats", "goats", "bulls"]} />
</Heading>
</Box>
</Center>
);
}
const rootElement = document.getElementById("root");
const root = createRoot(rootElement);
root.render(
<React.StrictMode>
<ChakraProvider>
<App />
</ChakraProvider>
</React.StrictMode>
);
and here is a link to the CodeSandbox I created