I’m trying to build a website using React and framer motion and I’m running into a challenge.
If an element is in the viewport when the page initially loads, I want there to be a delay on the animation. But if the element isn’t in the viewport, and the user has to scroll to see it, I want there to be a different delay value.
So for example, on my website I have a header Lorem ipsum and some text that I render first. Then I have a Projects header that renders second. And lastly, a My Project block that renders third.
This looks great if the window height is big enough so that everything loads in at once.
But if the window height is smaller, like in the GIF below, the My Project block takes too long to load in.
I’d like to have it so that it loads in faster like this:
I tried searching online to see if someone has had this problem before, but I couldn’t find any posts online about this.
Any ideas on how to solve this?
My code:
// page.tsx
<main>
<div>
<motion.h1
initial={{ opacity: 0, y: 10 }}
whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true }}
transition={{ ease: 'linear', duration: 0.5, delay: 0.15 }}
>
Lorem ipsum
</motion.h1>
<motion.div
initial={{ opacity: 0, y: 10 }}
whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true }}
transition={{ ease: 'linear', duration: 0.5, delay: 0.15 }}
>
<h1>
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod
tempor incididunt ut labore
</h1>
<h1>
Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris
</h1>
</motion.div>
</div>
<motion.h2
initial={{ opacity: 0, y: 10 }}
animate={{ opacity: 1, y: 0 }}
transition={{ ease: 'linear', duration: 0.5, delay: 0.8 }}
>
Projects
</motion.h2>
<div>
<Card
title={cards[0].title}
description={cards[0].description}
index={0}
/>
</div>
</main>
// Card.tsx
type CardProps = {
title: string;
description: string;
index: number;
};
export default function Card({
title,
image,
altText,
slug,
index,
description,
}: CardProps) {
delay = 1.05 + index * 0.2; // default delay
// delay = 0.25; // delay I want if it's not in the viewport on first page load
return (
<motion.div
initial={{ opacity: 0, y: 10 }}
whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true }}
transition={{
ease: 'linear',
duration: 0.5,
delay: delay,
}}
>
<p>{title}</p>
<p>{description}</p>
<a>
Learn more
<svg>...</svg>
</a>
</motion.div>
);
}



