I'm trying to make a carousel using Framer Motion in React, but I am getting a visual bug. The previous component isn't being unmounted before the new one is, and I am getting a weird effect.this Gyazo
This is the component handling everything:
const ProjectList = props => {
const [page, setPage] = useState(0);
const projects = [
<Project
name="Example"
desc="Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Libero nunc consequat interdum varius sit amet."
image={require("../img/office.jpg")}
/>,
<Project
name="Example2"
desc="Another example. This one does nothing too. What a suprise!"
image={require("../img/office.jpg")}
/>
]
const paginate = (newPage) => {
if(newPage < 0) {
setPage(projects.length - 1)
} else if (newPage > projects.length - 1) {
setPage(0);
} else {
setPage(newPage);
}
}
return (
<div className="project-list">
<AnimatePresence>
<motion.button
key="previous"
onClick={() => paginate(page-1)}
className="carousel-btn"
whileHover={{scale: 1.5, transition: {duration: 0.5}}}
>
<ArrowBackIosIcon/>
</motion.button>
<motion.div
key={page}
initial={{opacity: 0}}
animate={{opacity: 1}}
exit={{opacity: 0}}
>
{projects[page]}
</motion.div>
<motion.button
key="next"
onClick={() => paginate(page+1)}
className="carousel-btn"
whileHover={{scale: 1.5, transition: {duration: 0.5}}}>
<ArrowForwardIosIcon/>
</motion.button>
</AnimatePresence>
</div>
);
};
I'm not sure how to use libraries like framer-motion in the snippet tool, so here's a stripped down version on CodeSandbox
If you want one component to finish its unmount (exit
) animation before the mount animation of the next one starts, you’ll have to switch on AnimatePresence’s exitBeforeEnter
.
https://www.framer.com/api/motion/animate-presence/#animatepresenceprops.exitbeforeenter
Don’t know about that visual bug, because it’s not visible in the CodeSandbox example. But it might be because the component is (momentarily) removed from the DOM, which causes things to reorder. A solution might be to only put the motion.div
holding the image inside AnimatePresence
, as I did here: