I’m trying to implement a section which actually has the same logic as in brainsave.ai, the third section with Core Feature. In my case, the right side is replaced with bubbles and left side is pretty similar to the reference.
However, I’m having a problem with scroll animation as in our project it just goes to third index if I try to scroll a bit and animation is not right completely.
I’m using gsap
to implement this in React application. So, I have some state to track several things:
const bulletRef = useRef(null);
const [isLargeScreen, setIsLargeScreen] = useState(false);
const textRefs = useRef([]); // array to hold the elements on the left
const bubbleRefs = useRef([]); // array to hold the elements on the right
const mainS = useRef(null); // main section ref
const [activeDot, setActiveDot] = useState(0); // to check the current bulletpoint
I’m using useEffect
to trigger the scroll for gsap
:
useEffect(() => {
if (isLargeScreen) {
const tl = gsap.timeline({
scrollTrigger: {
trigger: mainS.current,
start: "top top",
scrub: 5,
pin: true,
},
});
textRefs.current.forEach((text, i) => {
const nexttext = textRefs.current[i + 1];
if (!(i === textRefs.current.length - 1)) {
tl.to(
text,
{
top: "-110%",
duration: 2,
ease: "power3.inOut",
opacity: 0,
},
`${i}key`
).to(
nexttext,
{
// delay: 0,
duration: 2.1,
top: 0,
ease: "power3.inOut",
},
`${i}key`
);
}
});
bubbleRefs.current.forEach((bubble, i) => {
const nextBubble = bubbleRefs.current[i + 1];
if (!(i === bubbleRefs.current.length - 1)) {
tl.to(
bubble,
{
top: "150%",
duration: 2,
ease: "power3.inOut",
opacity: 0,
},
`${i}key`
).to(
nextBubble,
{
// delay: 0,
duration: 2.1,
top: 0,
ease: "power3.inOut",
},
`${i}key`
);
}
});
}
}, [isLargeScreen]);
and I also have useEffect
for getting the screen size and setting isLargeScreen
.
and I return the code:
return (
<ReactLenis root options={lenisOptions}>
<div className={style.main}>
<section className={style.work} ref={mainS}>
{isLargeScreen ? (
<>
<div className={style.work_left}>
<div className={style.work_text}>
{WORK_ITEMS.map((item, index) => (
<div
id={`slide_${index}`}
ref={(el) => textRefs.current.push(el)}
key={index}
className={`text${index} ${style.work_info}`}
>
<div className={style.work_left_bl}>
<div className={style.core_btn}>
<div className={style.feature_btn}>Core Feature</div>
<h4 className={style.work_num}>{item.num}</h4>
</div>
<div className={style.svg_container}>
<h2 className={style.title}>{item.title}</h2>
<div className={style.desc}>{item.description}</div>
</div>
</div>
<div className={style.left_side_logo}>
{LOGO_LIST.map((i, e) => (
<img src={`/images/${i}`} />
))}
</div>
</div>
))}
{/* </ReactLenis> */}
</div>
<div className={style.pagination}>
{WORK_ITEMS.map((item, index) => (
<div
key={index}
className={`${style.dot} ${activeDot === index ? style.active : ""
}`}
onClick={() => handleDotClick(index)}
></div>
))}
</div>
</div>
<div className={style.work_right} ref={bulletRef}>
<div className={style.work_right_b1}>
<div className={style.container}>
{SECTION_CONTENT.map((group, groupIndex) => (
<div
ref={(el) => bubbleRefs.current.push(el)}
className={`bubble${groupIndex} ${style.bubbles_container}`}
>
{group.map((item, itemIndex) => (
<div
key={`${groupIndex}-${itemIndex}`}
className={` ${style.bubble} ${style[`bubble${itemIndex + 1}`]
}`}
>
{item}
</div>
))}
</div>
))}
</div>
</div>
</div>
</>
) : (
<div className={style.mobile_view}>
{WORK_ITEMS.map((item, index) => (
<div key={index} className={style.mobile_feature}>
<div className={style.work_info}>
<div className={style.work_left_bl}>
<div className={style.core_btn}>
<div className={style.feature_btn}>Core Feature</div>
<h4 className={style.work_num}>{item.num}</h4>
</div>
<div className={style.svg_container}>
<h2 className={style.title}>{item.title}</h2>
<div className={style.desc}>{item.description}</div>
</div>
</div>
</div>
<div className={style.container}>
{SECTION_CONTENT[index].map((e, i) => (
<div
className={`${style.bubble} ${style[`bubble${i + 1}`]}`}
>
{e}
</div>
))}
</div>
<div className={style.left_side_logo}>
{LOGO_LIST.map((i, e) => (
<img src={i} />
))}
</div>
</div>
))}
</div>
)}
</section>
</div>
</ReactLenis>
);
So, it check if the screen is either larger or small to place the content correctly.
I’m having following issues though:
0. it should take whole height (100vh
) if the section and once I’m in first element it should scroll within the elements of array (same as in brainsave.ai
- animation is not correct: when I scroll it just goes too fast and does not stop in second index, but goes up to third
- clicking to bulletpoint does not work right: it just makes the animation crazy while going to next/previous item
- responsiveness does not really work: if I make the screen smaller then content on the right side is having problem with placement
I have added the code to codesandbox if you would like to check it.