Jump to content
Search Community

Responsiveness with Scroll Trigger and image layers (Next.js)

Daniela Vornic test
Moderator Tag

Recommended Posts

Hi.

 

I'm wondering, what are the best practices for making the page below responsive, even only for desktop screens (width  > 1024px)? Currently, it works perfectly for 1920x919 browser screens. It uses Scroll Trigger for parallax scrolling with various images as layers. Any deviance of screen width and height has a huge impact on how the page looks. I realize that having different images of different sizes is one way to improve this perhaps, but what about vertical scrolling?

 

Thank you.

 

/* eslint-disable @next/next/no-img-element */
import { useEffect, useLayoutEffect, useRef, useState } from "react";
import Head from "next/head";
import { gsap } from "gsap";
import { ScrollTrigger } from "gsap/dist/ScrollTrigger";
 
import { useShopifyData } from "@/hooks";
import { FloatingMenu, FooterProduct } from "@/components";
 
export default function Verdelore() {
const { shopifyPages } = useShopifyData();
 
const [isMenuOpen, setIsMenuOpen] = useState(false);
const [windowHeight, setWindowHeight] = useState(0);
 
const fullBgRef = useRef(null);
const scrollerRef = useRef(null);
const firstSectionRef = useRef(null);
const secondSectionRef = useRef(null);
const thirdSectionRef = useRef(null);
const ffithSectionRef = useRef(null);
const footerRef = useRef(null);
const bgLeftRef = useRef(null);
const bgRightRef = useRef(null);
const bgFooterRef = useRef(null);
const elfRef = useRef(null);
const canRef = useRef(null);
const magicPotionTextRef = useRef(null);
const elixirTextRef = useRef(null);
 
const speed = windowHeight / 3.08;
 
useLayoutEffect(() => {
let ctx = gsap.context(() => {
gsap.registerPlugin(ScrollTrigger);
 
const tl = gsap.timeline({
scrollTrigger: {
trigger: ".scrollElement",
start: "top top",
end: 12000,
scrub: 5,
invalidateOnRefresh: true,
},
});
 
// Scroll past first title section
tl.to(fullBgRef.current, { y: -1.5 * speed, duration: 1.5 }, 0);
tl.to(firstSectionRef.current, { y: -2.5 * speed, opacity: 0, duration: 1.5 }, 0);
 
// Scroll to second section
tl.fromTo(
bgLeftRef.current,
{ y: 0, opacity: 0.9, x: -5 * speed, ease: "power1.out" },
{ y: -3.1 * speed, opacity: 1, x: 0, duration: 2 },
0.08,
);
tl.fromTo(
bgRightRef.current,
{ y: 0, opacity: 0.9, x: 5 * speed, ease: "power1.out" },
{ y: -3 * speed, opacity: 1, x: 0, duration: 2 },
0.08,
);
tl.to(secondSectionRef.current, { y: -2.7 * speed, duration: 2 }, 0.06);
 
// Move slide 2 up
tl.to(secondSectionRef.current, { y: -8 * speed, duration: 1.8 }, "+=0.5");
 
// Scroll to third section
tl.to(fullBgRef.current, { y: -4 * speed, duration: 1.5 }, "<");
tl.to(bgLeftRef.current, { y: -5.8 * speed, duration: 2 }, "<");
tl.to(bgRightRef.current, { y: -8.25 * speed, duration: 2.7 }, "<");
tl.to(thirdSectionRef.current, { y: -5.25 * speed, duration: 1.8, delay: 0.8 }, "<");
 
// Scroll to fourth section
tl.to(bgRightRef.current, { y: -9.8 * speed, duration: 2 }, "+=0.5");
tl.to(bgLeftRef.current, { y: -7 * speed, duration: 2 }, "<");
tl.to(fullBgRef.current, { y: -4.6 * speed, duration: 2 }, "<");
tl.to(thirdSectionRef.current, { y: -8 * speed, duration: 2 }, "<");
tl.to(bgFooterRef.current, { y: -7.3 * speed, duration: 2.5 }, "<");
tl.fromTo(
elfRef.current,
{ y: 0, opacity: 0.3, x: 0, width: 250, duration: 2.5, ease: "power1.out" },
{ y: -9.2 * speed, opacity: 1, x: 1.1 * speed, width: "21vw", duration: 3, scale: 1 },
"<",
);
tl.to(elfRef.current, { x: 4 * speed, opacity: 0, duration: 2.25 }, "+=0.7");
 
// Scroll to fifth section
tl.fromTo(elixirTextRef.current, { x: 0, opacity: 0 }, { y: -9 * speed, opacity: 0 }, "<");
tl.fromTo(
magicPotionTextRef.current,
{ x: 0, opacity: 0 },
{ y: -9 * speed, opacity: 0 },
"<",
);
tl.to(canRef.current, { y: -9 * speed, opacity: 0 }, "<");
tl.to(bgFooterRef.current, { y: -10.5 * speed, duration: 2 }, "-=1.3");
tl.to(elixirTextRef.current, { x: 0.7 * speed, opacity: 1, duration: 2.5 }, "-=1.1");
tl.to(
magicPotionTextRef.current,
{ x: 0.7 * speed, opacity: 1, duration: 2.5, delay: 0.2 },
"<",
);
tl.fromTo(
canRef.current,
{ x: 1 * speed, opacity: 0, width: "100px" },
{ width: "13vw", opacity: 1, duration: 2.5 },
"<",
);
 
// Scroll to footer
tl.to(ffithSectionRef.current, { y: -14 * speed, duration: 5 }, "+=0.5");
tl.to(bgFooterRef.current, { y: -14 * speed, duration: 3 }, "<");
tl.to(footerRef.current, { y: -11.5 * speed, duration: 3 }, "-=5.75");
});
 
return () => {
ctx.revert();
};
}, [speed]);
 
useEffect(() => {
const handleResize = () => {
setWindowHeight(window.innerHeight);
};
 
window.addEventListener("resize", handleResize);
 
return () => window.removeEventListener("resize", handleResize);
}, []);
 
return (
<>
<Head>
<title>Verdelore | Elven Springs</title>
<meta name="description" content="Verdelore" />
</Head>
<div className="wrapper relative z-40 h-[205vh] overflow-hidden bg-bg">
<main className="content" ref={scrollerRef}>
<img
src="/assets/images/center.png"
alt="bg"
className="bg absolute w-full"
ref={fullBgRef}
/>
 
<section
className="relative z-50 flex h-screen flex-col items-center justify-center"
ref={firstSectionRef}
>
<FloatingMenu isMenuOpen={isMenuOpen} setIsMenuOpen={setIsMenuOpen} />
<div>
<img src="/assets/images/logo.png" alt="logo" className="h-fit w-[500px]" />
</div>
</section>
 
<img
src="/assets/images/bg-left.png"
alt="bg"
className="absolute left-0 w-[25vw]"
ref={bgLeftRef}
/>
 
<img
src="/assets/images/bg-right-full.png"
alt="bg"
className="absolute right-0 w-[100vw]"
ref={bgRightRef}
/>
 
<section
className="relative z-20 flex h-screen flex-col items-center text-center"
ref={secondSectionRef}
>
<h1 className="mb-6 font-heading text-[3.6vw] text-secondary">
{shopifyPages?.verdelore?.title}
</h1>
<p
className="max-w-lg font-dunbar text-[1.33vw] font-medium leading-[1.33] xl:max-w-xl 2xl:max-w-3xl"
dangerouslySetInnerHTML={{ __html: shopifyPages?.verdelore?.body as TrustedHTML }}
/>
</section>
 
<section
className="relative z-10 flex h-screen flex-col items-center text-center"
ref={thirdSectionRef}
>
<h2 className="mb-6 font-heading text-[3.6vw] text-black">
{shopifyPages?.forasdal?.title}
</h2>
<p
className="max-w-lg font-dunbar text-[1.33vw] font-medium leading-[1.33] xl:max-w-xl 2xl:max-w-3xl"
dangerouslySetInnerHTML={{ __html: shopifyPages?.forasdal?.body as TrustedHTML }}
/>
</section>
 
<img src="/assets/images/elf.png" alt="bg" className="absolute left-0" ref={elfRef} />
 
<img
src="/assets/images/bg-footer.png"
alt="bg"
className="absolute left-0 w-full"
ref={bgFooterRef}
/>
 
<section className="relative z-10 flex h-screen" ref={ffithSectionRef}>
<div className="max-w-[40vw] space-y-10 pt-5">
<div className="space-y-4" ref={magicPotionTextRef}>
<h2 className="pl-[36px] font-heading text-[2.1vw] uppercase leading-[104px] text-aqua">
{shopifyPages?.["magic-potion"]?.title}
</h2>
<p
className="font-dunbar text-[1.15vw] text-aqua"
dangerouslySetInnerHTML={{
__html: shopifyPages?.["magic-potion"]?.body as TrustedHTML,
}}
/>
</div>
 
<div className="space-y-10" ref={elixirTextRef}>
<div className="space-y-4">
<h2 className="pl-[36px] font-heading text-[2.1vw] uppercase leading-[104px] text-aqua">
{shopifyPages?.elexir?.title}
</h2>
<p
className="font-dunbar text-[1.15vw] text-aqua"
dangerouslySetInnerHTML={{
__html: shopifyPages?.elexir?.body as TrustedHTML,
}}
/>
</div>
<a
href="https://elvensprings.myshopify.com/products/elven-spring-water-19-2-oz-king-size-cans-12-pack"
className="ml-[36px] block w-fit rounded-full border-3 border-green bg-white bg-opacity-30 px-7 py-2 font-dunbar text-xs font-bold uppercase text-green transition hover:bg-opacity-0 sm:mt-4 sm:text-sm xl:px-16 xl:py-2 xl:text-lg 2xl:px-20 2xl:py-4 2xl:text-xl"
target="_blank"
rel="noreferrer"
>
Pre-order
</a>
</div>
</div>
 
<img
src="/assets/images/verdelore-can.png"
alt="bg"
className="h-fit w-full"
ref={canRef}
/>
</section>
 
<section className="absolute w-full" ref={footerRef}>
<FooterProduct />
</section>
</main>
</div>
 
<div className="scrollElement" />
</>
);
}
Link to comment
Share on other sites

Hi @Daniela Vornic welcome to the forum! 

 

Have you seen gsap.matchMedia() with it you can use CSS like media queries in your JS and get some really nice features that hook directly into GSAP!

 

Hope this answers your question, if not can you please provide a minimal demo so that we can take a look at your code in action! Check out our Stackblitz starter templates and the  React/Next(please read this article!) templates. Hope it helps and happy tweening!  

 

  • Like 1
Link to comment
Share on other sites

9 minutes ago, mvaneijgen said:

Hi @Daniela Vornic welcome to the forum! 

 

Have you seen gsap.matchMedia() with it you can use CSS like media queries in your JS and get some really nice features that hook directly into GSAP!

 

Hope this answers your question, if not can you please provide a minimal demo so that we can take a look at your code in action! Check out our Stackblitz starter templates and the  React/Next(please read this article!) templates. Hope it helps and happy tweening!  

 

Thank you for your answer.

 

However, I believe using matchMedia would be extremely tedious since I'd have to include so many screen sizes... I was thinking to use some coefficients for the y and x values in GSAP, but I am lost at the moment on how to arrive at that.

 

Here is a demo: https://elv.ixobit.md/verdelore/

It works as intended only for 1920x919 browser screens.

Link to comment
Share on other sites

gsap.matchMedia() is really powerful and you can set it up what best fits your use case. If reading is less your thing check out this video where @Cassie walks you through most of the features and logic 

 

 

5 minutes ago, Daniela Vornic said:

Sadly we can't debug a 'live' website, there is just no way to modify the code (or even find it). If you need more help can you make a minimal demo, so that we can see your code in action. Please don't include your whole project, just the relevant parts. 

 

  • Like 3
Link to comment
Share on other sites

Yeah, give that article a read - looks like you could just use viewport units (vh) rather than pixels, or yPercent rather than y?

Using the right values from the start prevents headaches in a lot of cases.

(I'll let you reply now @mvaneijgen - also hi hi 👋 been a while since I've been in the forums, miss you all! )

  • Haha 1
Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...