Reputation: 320
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>
);
}
Upvotes: 0
Views: 49
Reputation: 320
I solved it! It took me a while to think of a solution because useInView
and IntersectionObserver
will tell you when something enters the viewport, but they don't tell you if an element is in the view port on first page load.
The solution is surprisingly simple and concise:
const ref = useRef<HTMLDivElement>(null);
const [delay, setDelay] = useState(1.05 + index * 0.2);
useEffect(() => {
if (ref.current) {
if (ref.current.getBoundingClientRect().top > window.innerHeight) {
setDelay(0.25);
}
}
}, []);
return (
<motion.div
ref={ref}
initial={{ opacity: 0, y: 10 }}
whileInView={{ opacity: 1, y: 0 }}
viewport={{ once: true }}
transition={{
ease: 'linear',
duration: 0.5,
delay: delay,
}}
>
// ...
)
Upvotes: 0