Jump to content
Search Community

How to implement infinite scroll to React project?

Serhat Düzgün test
Moderator Tag

Recommended Posts

Hello all.

I want to add an unlimited, ongoing scroll to a section in my project with GSAP, but I can't do it. Since I'm working with React, I haven't come across any examples. I've tried with many examples in this forum but I found one working but I can't get it to work properly either.
 
It's not fluent. It's annoying when scrolling. I couldn't add a Codepen project, but I think the video will be enough. I will leave the codes below the video. Can you please help?

 

 

Sorry for the quality, I compressed it because the upload limit

 

 

import React, { useEffect, useRef } from "react";
import gsap from "gsap";
import ScrollTrigger from "gsap/ScrollTrigger";
import SplitText from "gsap/SplitText";
 
import "../styles/work.scss";
import NavMenu from "../components/NavBar/NavMenu";
 
gsap.registerPlugin(ScrollTrigger, SplitText);
 
const Work = () => {
const workOne = useRef();
const workTwo = useRef();
const workThree = useRef();
const workFour = useRef();
const tl = gsap.timeline({ paused: true });
const tl2 = gsap.timeline({ paused: true });
const tl3 = gsap.timeline({ paused: true });
const tl4 = gsap.timeline({ paused: true });
 
useEffect(() => {
const splitWorkOne = new SplitText(".workone-title", { type: "chars" });
const splitWorkTwo = new SplitText(".worktwo-title", { type: "chars" });
const splitWorkThree = new SplitText(".workthree-title", { type: "chars" });
const splitWorkFour = new SplitText(".workfour-title", { type: "chars" });
 
tl.to(workOne.current, { filter: "grayscale(100%)" });
tl.to(workOne.current, { scale: 0.99 });
tl.to(splitWorkOne.chars, { y: -40, stagger: 0.015 }, "<");
 
tl2.to(workTwo.current, { filter: "grayscale(100%)" });
tl2.to(workTwo.current, { scale: 0.99 });
tl2.to(splitWorkTwo.chars, { y: -40, stagger: 0.015 }, "<");
 
tl3.to(workThree.current, { filter: "grayscale(100%)" });
tl3.to(workThree.current, { scale: 0.99 });
tl3.to(splitWorkThree.chars, { y: -40, stagger: 0.015 }, "<");
 
tl4.to(workFour.current, { filter: "grayscale(100%)" });
tl4.to(workFour.current, { scale: 0.99 });
tl4.to(splitWorkFour.chars, { y: -40, stagger: 0.015 }, "<");
 
// Infinite scroll
let panels = gsap.utils.toArray(".panel"),
copy = panels[0].cloneNode(true);
panels[0].parentNode.appendChild(copy); // copy the first panel to the end (for seamless looping)
 
panels.forEach((panel, i) => {
ScrollTrigger.create({
trigger: ".menu",
start: "top top",
});
});
 
let maxScroll;
let pageScrollTrigger = ScrollTrigger.create({
// snap whole page to the closest section!
// normally we'd just do snap: 1 / panels.length but we'll use a function-based value so that we can handle the very start and end values in a special way to prevent looping on the snap
snap(value) {
let snappedValue = gsap.utils.snap(1 / panels.length, value);
if (snappedValue <= 0) {
// don't let it go all the way back to exactly 0 or it'll wrap. Keep it a bit more than 1px from the top.
return 1.05 / maxScroll;
} else if (snappedValue >= 1) {
// don't let it go all the way to the end or it'll wrap. Keep it a bit more than 1px from the bottom.
return maxScroll / (maxScroll + 1.05);
}
return snappedValue;
},
});
function onResize() {
maxScroll = ScrollTrigger.maxScroll(window) - 1;
}
onResize();
window.addEventListener("resize", onResize);
// make sure we use a non-passive event listener so that we can preventDefault() on the scroll if it's at the very top or bottom
window.addEventListener(
"scroll",
(e) => {
let scroll = pageScrollTrigger.scroll();
if (scroll > maxScroll) {
pageScrollTrigger.scroll(1);
e.preventDefault();
} else if (scroll < 1) {
pageScrollTrigger.scroll(maxScroll - 1);
e.preventDefault();
}
},
{ passive: false }
);
}, [tl, tl2, tl3, tl4]);
 
useEffect(() => {}, []);
 
const workOneOverHandler = () => {
tl.play();
};
const workOneLeftHandler = () => {
tl.reverse();
};
 
const workTwoOverHandler = () => {
tl2.play();
};
const workTwoLeftHandler = () => {
tl2.reverse();
};
 
const workThreeOverHandler = () => {
tl3.play();
};
const workThreeLeftHandler = () => {
tl3.reverse();
};
 
const workFourOverHandler = () => {
tl4.play();
};
const workFourLeftHandler = () => {
tl4.reverse();
};
 
return (
<div className="work-page">
<div className="work-navmenu">
<NavMenu />
</div>
<div className="menu">
<div className="panel">
<div className="work-two-three-section">
<div
className="work-two"
ref={workTwo}
onMouseEnter={workTwoOverHandler}
onMouseLeave={workTwoLeftHandler}
>
<div className="worktwo-title-wrapper">
<h1 className="worktwo-title">
<span></span>Project: Stationery
</h1>
</div>
</div>
<div
className="work-three"
ref={workThree}
onMouseEnter={workThreeOverHandler}
onMouseLeave={workThreeLeftHandler}
>
<div className="workthree-title-wrapper">
<h1 className="workthree-title">
<span></span>Project: *******
</h1>
</div>
</div>
</div>
<div className="work-four-section">
<div
className="work-four"
ref={workFour}
onMouseEnter={workFourOverHandler}
onMouseLeave={workFourLeftHandler}
/>
<div className="workfour-title-wrapper">
<h1 className="workfour-title">
<span></span>Project: HANDZ
</h1>
</div>
</div>
</div>
<div className="panel">
<div className="header-section">
<h2 className="recent">RECENT</h2>
<div className="words-wrapper">
<h2 className="works">WORKS</h2>
<h2 className="year">
<span>2</span>
<span>0</span>
<span>2</span>
<span>2</span>
</h2>
</div>
</div>
<div className="work-one-section">
<div
className="work-one"
ref={workOne}
onMouseEnter={workOneOverHandler}
onMouseLeave={workOneLeftHandler}
/>
<div className="workone-title-wrapper">
<h1 className="workone-title">
<span></span>Project: AAART
</h1>
</div>
</div>
</div>
</div>
</div>
);
};
 
export default Work;  

 

 

Link to comment
Share on other sites

We love helping with GSAP-related questions, but unfortunately we just don't have the resources to provide free general consulting, logic troubleshooting, or "here is my list of requirements ___ please show me how to build it" tutorials. Of course anyone else is welcome to post an answer if they'd like - we just want to manage expectations.  

 

You can post in the "Jobs & Freelance" forum for paid consulting, or contact us directly. 

 

Otherwise, if you've got a GSAP-specific question just post that here along with a minimal demo and we'd be happy to take a look. 

 

Here's a React starter template on CodeSandbox that you can fork.

 

Once we see an isolated demo, we'll do our best to jump in and help with your GSAP-specific questions. 

 

By the way, it looks like a lot of your code is repetitive and could benefit from a .forEach() loop to simplify it. Also remember that React re-renders things and calls chunks of that code multiple times in various situations, so it's important for you to understand the framework you're working with. This might help:

 

Good luck!

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...