I want to achieve the animation in the following link: https://www.hover.dev/components/cards#reveal-cards
But I'm using framer motion. I was able to recreate it, but the exit animation jitters some how, it's not smooth as in the example.
I tried using plain css, but it didn't yield the needed result either.
Any help is appreciated.
Here's my code:
const PortfolioSection: FC = () => {
const list = [
{
title: "abc",
url: "https://www.example.com",
image: "https://images.unsplash.com/photo-1488972685288-c3fd157d7c7a?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=1740&q=80"
},
{
title: "klm",
url: "https://www.example.com",
image: "https://images.unsplash.com/photo-1487958449943-2429e8be8625?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=1740&q=80"
},
{
title: "ost",
url: "https://www.example.com",
image: "https://images.unsplash.com/photo-1449157291145-7efd050a4d0e?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=1740&q=80"
},
{
title: "xyz",
url: "https://www.example.com",
image: "https://images.unsplash.com/photo-1598818384697-62330d600309?ixlib=rb-4.0.3&ixid=M3wxMjA3fDB8MHxwaG90by1wYWdlfHx8fGVufDB8fHx8fA%3D%3D&auto=format&fit=crop&w=687&q=80"
}
];
const imgMotion = {
initial: {
top: "0%",
right: "0%",
scale: 1
},
hover: {
top: "25%",
right: "25%",
scale: 0.5,
transition: {
duration: 0.3,
// type: "tween",
// ease: "easeIn",
all: { type: "tween", damping: 5, stiffness: 500 }
}
}
};
return (
<HomePageSection>
<Grid
container
rowSpacing={4}
columnSpacing={2}
justifyContent="center"
sx={{ height: "100%" }}
>
{list.map((item, i) => (
<Grid
key={i}
size={{ md: 6, xs: 12 }}
>
<motion.div
initial="initial"
// animate="initial"
whileHover="hover"
whileTap="hover"
exit="exit"
style={{
width: "100%",
height: "30vh",
position: "relative",
border: "4px solid green"
}}
>
<div
style={{
height: "50%",
display: "grid",
placeContent: "center",
textTransform: "uppercase",
backgroundColor: "black",
color: "white"
}}
>
<Typography variant="h4">
{ item.title }
</Typography>
</div>
<motion.div
variants={imgMotion}
style={{
height: "100%",
width: "100%",
backgroundImage: `url(${item.image})`,
backgroundSize: "cover",
backgroundPosition: "center center",
zIndex: "10",
position: "absolute"
}}
/>
<Link
href={item.url}
target="_blank"
style={{
position: "absolute",
bottom: 0,
right: 0,
height: "50%",
width: "50%",
display: "grid",
placeContent: "center",
backgroundColor: "black"
}}
>
Read more
</Link>
</motion.div>
</Grid>
))}
</Grid>
</HomePageSection>
);
};
The reason your exit animation could be glitchy is because you don't have an exit variant defined in your imgMotion
. However, you don't need the exit variant, as that is only really useful when using AnimatePresence
. You're implementation is close, but I changed a couple things to make it work a bit better. You can find a working example here (I also recreated the effect with pure CSS and TailwindCSS). See the fix in the comments in the code below:
// Instead of animating the left and right properties,
// animate the width and height. This is much
// simpler and seems to perform better
const imgMotion: Variants = {
initial: {
width: '100%',
height: '100%',
},
hover: {
width: '50%',
height: '50%',
transition: {
duration: 0.3,
// type: "tween",
// ease: "easeIn",
all: { type: 'tween', damping: 5, stiffness: 500 },
},
},
};
...
<motion.div
initial="initial"
whileHover="hover"
whileTap="hover"
// Remove the exit variant, it's unnecessary and
// only really works with AnimatePresence
style={{
...existing styles
}}
>
<div
style={{
...existing styles
}}
>
<h4>{item.title}</h4>
</div>
<motion.div
variants={imgMotion}
style={{
...existing styles,
// Set the image to be anchored to the bottom left of the card
bottom: 0,
left: 0,
}}
/>
<a
href={item.url}
target="_blank"
style={{
...existing styles
}}
>
Read more
</a>
</motion.div>