Talukder Habib Riyadh
Talukder Habib Riyadh

Reputation: 187

Flickering and Fast-Scrolling Issues with GSAP ScrollTrigger on Safari for Footer Reveal Animation

I’m working on a Next.js project with GSAP free version, where I’ve implemented a footer reveal animation inspired by the behavior on the Raxo website. The goal is to make the footer overlap the main content when a specific section () becomes visible at the bottom of the viewport.

Here’s a brief description of my setup:

The main content is pinned while scrolling. The footer slides up and overlaps the main content as the user scrolls to the end. I’m using GSAP ScrollTrigger for the animations and react for component structure. You can find my implementation on CodeSandbox: 👉 CodeSandbox Link

Problems:

  1. Pinning Issue: The main content pinning is not smooth, especially during the transition when the footer starts overlapping the main content. The transition feels jittery and unstable.

  2. Footer Flickering: The footer flickers significantly on Safari during the reveal animation. While the flickering in Chrome is minimal and tolerable, it’s highly noticeable and disruptive on Safari (both macOS and iOS).

  3. White Area Beneath Footer on Fast Scrolling: During fast scrolling, the pinned footer (set to 100vh) moves slightly, and a white area becomes visible beneath the footer. It seems like the footer isn’t fully pinned to the bottom during these fast scroll events.

Relevant Code:

"use client";
import { useEffect, useRef } from "react";
import gsap from "gsap";
import { ScrollTrigger } from "gsap/ScrollTrigger";

gsap.registerPlugin(ScrollTrigger);

export default function CustomLayout({ children }) {
  const titleRef = useRef(null);
  const containerRef = useRef(null);
  const mainContentRef = useRef(null);

  useEffect(() => {
   if (!titleRef.current || !containerRef.current || !mainContentRef.current) return;

   const title = titleRef.current;
   const container = containerRef.current;
   const mainContent = mainContentRef.current;

   gsap.set(container, { y: "100%", force3D: true, backfaceVisibility: "hidden" });
   gsap.set(mainContent, { opacity: 1, scale: 1, force3D: true, backfaceVisibility: "hidden" });

   const timeline = gsap.timeline()
      .to(mainContent, {
        opacity: 1,
        scale: 0.99,
        duration: 1,
        ease: "power3.out",
      })
      .to(mainContent, {
        y: "-2%",
        duration: 0.5,
        ease: "power2.inOut",
      });

   ScrollTrigger.create({
     trigger: title,
     start: "bottom bottom",
     end: () => `+=${container.getBoundingClientRect().height}`,
     pin: mainContent,
     animation: timeline,
     pinSpacing: false,
     scrub: 1,
     onEnter: () => {
       gsap.to(container, {
         y: 0,
         duration: 1,
         ease: "power4.out",
         clearProps: "transform",
        });
      },
      onLeaveBack: () => {
        gsap.to(container, {
          y: "100%",
          duration: 1,
          ease: "power4.out",
          clearProps: "transform",
        });
      },
    });

   ScrollTrigger.refresh();

   return () => {
      ScrollTrigger.killAll();
    };
  }, []);

  return (
    <div className="flex flex-col">
      <header>Header Content</header>
      <main ref={mainContentRef} className="flex-grow bg-gray-100">
        {children}
        <div ref={titleRef} className="h-[1px] opacity-0 bg-transparent flex items-center">
          <p className="relative lg:absolute h-0 w-full bg-red-500 text-5xl font-bold text-white text-center">
            Footer Title
          </p>
        </div>
      </main>
      <footer
        style={{
          minHeight: "100vh",
          transform: "translateZ(0)",
          willChange: "transform",
        }}
        ref={containerRef}
        className="lg:relative bg-black text-white h-[calc(100vh+180px)] sm:h-auto z-[2] overflow-hidden"
      >
        Footer Content
      </footer>
    </div>
  );
}

What I've Tried:

Using force3D and backfaceVisibility:

Added force3D: true and backfaceVisibility: hidden to both the footer and main content, but these didn’t resolve the flickering, pinning smoothness, or the white area issue.

Tuning ScrollTrigger Configurations:

Adjusted the start, end, and pinSpacing values for ScrollTrigger. Experimented with invalidateOnRefresh, fastScrollEnd, and easing functions without success.

Testing with or Without Hardware Acceleration in CSS:

Tried enabling/disabling transform: translateZ(0) and will-change: transform, but the flickering and white area issues persist.

Adjusting Duration and Scrub:

Changed the animation duration and scrub values in GSAP. This slightly improved the pinning but didn’t eliminate the white gap during fast scrolls.

Lenis for smooth scrolling:

I have used Lenis for smooth scrolling. But using it cause more flickring in Safari browser.

Environment:

Browser: Major problem in Safari (macOS and iOS). GSAP Version: Free version ([email protected]). React: [email protected]. Framework: Next.js.

Expected Behavior:

The main content should pin smoothly during scrolling, and the footer should reveal without any flickering or jittering. During fast scrolling, the footer should maintain its position without revealing any white area beneath it.

References:

Similar footer behavior: https://raxo.co/

Upvotes: 8

Views: 153

Answers (0)

Related Questions