Azzie Fuzzie Posted September 26, 2025 Posted September 26, 2025 Hi GSAP team, I’m running into an issue with ScrollTrigger pinning. I have a section with a .menu__image__wrapper that I want to pin while scrolling. On first page load, the pin doesn’t work (the image disappears). If I switch categories in my app (which destroys and re-inits the triggers), the pin suddenly works correctly. I also noticed earlier that I had a y transform (GSAP.set(..., { y: ... })) on the pinned element itself. Removing that transform fixed some glitching, but I still sometimes see the pin fail on first load. Video Of Issue:https://www.loom.com/share/f02251051e0d40a5b0595020b76bd8b7?sid=2a13507c-e9aa-4d87-9644-47d2f085eb01 Here’s a simplified version of my setup: Here is the animation class code for the menuPIN only: import GSAP from "gsap"; import { ScrollTrigger } from "gsap/ScrollTrigger"; GSAP.registerPlugin(ScrollTrigger); export default class MenuPin { constructor() { this.mm = null; // matchMedia instance this.triggers = []; // ScrollTrigger instances this.clickHandlers = []; // mobile click handlers this.currentSection = null; } setupSection(section) { if (!section) return; const runSetup = () => { // destroy previous triggers/spacers this.destroy(); this.currentSection = section; this.mm = GSAP.matchMedia(); const initDesktop = () => { this.setupDesktop(section); }; // Desktop this.mm.add("(min-width:1024px)", () => { const firstImg = section.querySelector(".menu__image"); if (firstImg && !firstImg.complete) { firstImg.addEventListener("load", initDesktop, { once: true }); setTimeout(initDesktop, 300); } else { initDesktop(); } return () => { }; }); // Mobile this.mm.add("(max-width:1023px)", () => { requestAnimationFrame(() => this.setupMobile(section)); return () => { }; }); }; // Run immediately if DOM is ready, otherwise wait for DOMContentLoaded if (document.readyState === "loading") { document.addEventListener("DOMContentLoaded", runSetup); } else { runSetup(); } } setupDesktop(section) { const imageWrapper = section.querySelector(".menu__image__wrapper"); if (!imageWrapper) return; const images = Array.from(section.querySelectorAll(".menu__image")); const imageHeight = section.querySelector(".menu__image").getBoundingClientRect().height const items = Array.from(section.querySelectorAll(".menu__item")); const itemHeight = section.querySelector(".menu__item").getBoundingClientRect().height if (!items.length) return; // spacer at the end let spacer = section.querySelector(".menu__spacer"); if (!spacer) { spacer = document.createElement("div"); spacer.classList.add("menu__spacer"); section.appendChild(spacer); } spacer.style.height = `${imageHeight - itemHeight - 41}px`; const pinDuration = section.scrollHeight; // Pin image wrapper const pinTrigger = ScrollTrigger.create({ trigger: section, start: "top top", end: () => `+=${pinDuration}`, pin: imageWrapper, pinSpacing: false, markers: false, }); this.triggers.push(pinTrigger); // Set first image visible right away // GSAP.set(images[0], { autoAlpha: 1 }); // Then set up triggers items.forEach((item, i) => { const t = ScrollTrigger.create({ trigger: item, start: `top-=25 top`, end: `bottom-=25 top`, // onEnter: () => this.showImage(images, i), // onEnterBack: () => this.showImage(images, i), markers: true, }); this.triggers.push(t); }); } setupMobile(section) { const images = Array.from(section.querySelectorAll(".menu__image")); const items = Array.from(section.querySelectorAll(".menu__item")); // remove previous handlers items.forEach(item => { if (item._menuPinHandler) { item.removeEventListener("click", item._menuPinHandler); delete item._menuPinHandler; } }); // add click handlers items.forEach((item, i) => { const handler = () => this.showImage(images, i); item.addEventListener("click", handler); item._menuPinHandler = handler; this.clickHandlers.push({ el: item, handler }); }); // show first image // this.showImage(images, 0); } showImage(images, index) { images.forEach((img, i) => { GSAP.set(img, { autoAlpha: i === index ? 1 : 0, border: i === index ? "3px solid red" : "none" }); }); GSAP.to(images[index], { autoAlpha: 1, duration: 0.25, ease: "expo.out" }); } destroy() { // kill triggers this.triggers.forEach(t => t && t.kill()); this.triggers = []; // remove click handlers this.clickHandlers.forEach(({ el, handler }) => { el.removeEventListener("click", handler); if (el._menuPinHandler) delete el._menuPinHandler; }); this.clickHandlers = []; // remove spacer if (this.currentSection) { const sp = this.currentSection.querySelector(".menu__spacer"); if (sp) sp.remove(); } this.currentSection = null; } } import Page from '@classes/Page'; import MenuPin from '@animations/MenuPin'; import MobileCategorySwipe from '@animations/MobileCategorySwipe'; import GSAP from 'gsap'; import { ScrollTrigger } from "gsap/ScrollTrigger"; GSAP.registerPlugin(ScrollTrigger); export default class Menu extends Page { constructor() { super({ element: '.menu', elements: { wrapper: '.menu__wrapper', section: '.menu__section', button: '.menu__category__label', }, }); } create() { super.create(); this.menuPin = new MenuPin(); const categoryEl = document.querySelector(".menu__category"); if (categoryEl) { new MobileCategorySwipe({ element: categoryEl }); } const firstSection = document.querySelector(".menu__section.--active"); if (firstSection) { this.menuPin.setupSection(firstSection); } // bind buttons this.elements.button.forEach(btn => { btn.addEventListener("click", () => { const category = btn.dataset.category; this.showCategory(category); }); }); } showCategory(category) { // Hide all sections first this.elements.section.forEach(section => { const isActive = section.dataset.category === category; if (isActive) { section.classList.add('--active'); console.log(section); // Make section visible so ScrollTrigger can measure GSAP.set(section, { autoAlpha: 1 }); // Destroy old triggers before setting up the new one if (this.menuPin) this.menuPin.destroy(); // Setup the pin/animations for the new section this.menuPin.setupSection(section); // Refresh ScrollTrigger after layout changes requestAnimationFrame(() => ScrollTrigger.refresh()); } else { section.classList.remove('--active'); GSAP.set(section, { autoAlpha: 0 }); } }); // Update buttons this.elements.button.forEach(btn => { const isActive = btn.dataset.category === category; btn.classList.toggle('--active', isActive); GSAP.to(btn, { color: isActive ? "#000" : "#CBC4B1", duration: 0.5, ease: "expo.out" }); }); } }
GSAP Helper Posted September 26, 2025 Posted September 26, 2025 Without a minimal demo, it's very difficult to troubleshoot; the issue could be caused by CSS, markup, a third party library, a 3rd party script, etc. Would you please provide a very simple CodePen or Stackblitz that illustrates the issue? Please don't include your whole project. Just some colored <div> elements and the GSAP code is best. See if you can recreate the issue with as few dependencies as possible. Start minimal and then incrementally add code bit by bit until it breaks. Usually people solve their own issues during this process! If not, at least we have a reduced test case which greatly increases your chances of getting a relevant answer. See the Pen aYYOdN by GreenSock (@GreenSock) on CodePen. that loads all the plugins. Just click "fork" at the bottom right and make your minimal demo: Using a framework/library like React, Vue, Next, etc.? CodePen isn't always ideal for these tools, so here are some Stackblitz starter templates that you can fork and import GSAP as shown in the Install Helper in our Learning Center : React (please read this article!) Next Svelte Sveltekit Vue Nuxt Please share the StackBlitz link directly to the file in question (where you've put the GSAP code) so we don't need to hunt through all the files. Once we see an isolated demo, we'll do our best to jump in and help with your GSAP-specific questions. ✅
Azzie Fuzzie Posted September 27, 2025 Author Posted September 27, 2025 Thanks,I did upload only the code for that animation I tried doing a codepen its really difficult replicating the issue
Rodrigo Posted September 27, 2025 Posted September 27, 2025 Sorry to hear about that, but most likely it means that the problem is not really GSAP related but elsewhere in your setup. A codepen/stackblitz demo allows us not only to play with the code and preview changes and debug easier, but also to isolate the working environment to just HTML/CSS and GSAP. IDK what's the tech stack you're using, if you are using a framework or perhaps a CSS library or perhaps other packages that could be causing this. As already mentioned in this thread there could be a series of alternatives that might be causing this:
Recommended Posts
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 accountSign in
Already have an account? Sign in here.
Sign In Now