Jump to content
Search Community

Search the Community

Showing results for tags 'scrolltrigger'.

  • Search By Tags

    Type tags separated by commas.
  • Search By Author

Content Type


Forums

  • GreenSock Forums
    • GSAP
    • Banner Animation
    • Jobs & Freelance
  • Flash / ActionScript Archive
    • GSAP (Flash)
    • Loading (Flash)
    • TransformManager (Flash)

Product Groups

  • Club GreenSock
  • TransformManager
  • Supercharge

Categories

There are no results to display.


Find results in...

Find results that contain...


Date Created

  • Start

    End


Last Updated

  • Start

    End


Filter by number of...

Found 1,178 results

  1. Wild West Design

    1px border glitch on chrome and firefox jitternes

    Hello, I have two problems/issues: 1. Google Chrome sometimes does not render well 1px solid borders - i have a list of three columns and within them there is a element that has 1px solid border, in two of them the border looks glitchy like thicker that it should be. In the other one "thiner" looks like it should be without any glitches. Additionally this is random.. its not always the same ones rendered wrongly.. The code set up for this is: .section__fade-in-group > * { opacity: 0; will-change: opacity, transform; visibility: hidden; transform: translateY(-20px); } if (section.querySelector(".section__fade-in-group")) { sectionTL.to( section.querySelector(".section__fade-in-group").children, { autoAlpha: 1, y: 0, duration: 0.75, stagger: 0.15, ease: "expo.out", }, section.querySelector(".section__content") ? "-=0.5" : "" ); } So the animation is pretty simple.. Does anyone know why is that happening? 2. The second problem is the Firefox, i have a lot of similiar animations like in point 1 but in Firefox they are a bit jittery - not as smooth as in Google Chrome. For example: I have this animation for full width hero image: const imgCover = heroBanner.querySelector(".img-cover"); heroBannerTL.fromTo( imgCover, {y: -20, scale: 1.1}, { y: 0, scale: 1, opacity: 1, duration: 1.5, ease: "power3.out", } ); I have tried will-change and stuff but nothing helps.. is there anything that could smoothen? UPDATE: I have tried this: // Fix: pixel-snapping that Firefox applies for some reason during rendering (totally unrelated to GSAP). rotation: 0.1, and seems like it helped. thanks guyz!
  2. i am trying to create an effect where the card has an intro animation, then it has a certain arrangement and after some time has an outro animation. i want everything to be on a scrub. for the positioning of the elements i have tried to use the flip plugin. but i can't seem to make multiple ones work on scrub. everything seems to be playing all at once.
  3. Hi, I need some help with a split-text animation. I’m animating split text on scroll with staggered effects. The animation works as expected when scrolling down, but when scrolling back up, the first letter (or word) does not appear. I would like to understand why this is happening. Thank you.
  4. I’m trying to recreate the scroll-driven cards animation used on https://www.garcy.studio/ Target behaviour (reference attached via screenshot): Cards start offscreen (bottom / slight right). On scroll, the whole cards wrapper moves diagonally (up + left). Once the first card fully enters the viewport, the wrapper pins. While pinned, cards translate horizontally in a smooth, continuous way. No jumps, no resets, and no sudden position corrections after pinning. I’ve attached screenshots of the reference animation and shared a minimal CodePen demo showing the issue. Please help me here.
  5. Rhapsody

    ScrollTrigger animation 1 - Newbie 0

    Hello everyone, I am extremely new to GSAP, and want to create an animation where, when the user scrolls, "Web design" shows on the center, as they continue scrolling, it splits to show the description inside (top shows top/half on web design, bottom shows bottom half, ccntent in the center, max width being that of the title). As they continue scrolling, "Graphic Design" scrolls in horizontally (from the right), pushing "Web design off screen (to the left). On continued scroll, it too splits to reveal the description. On scroll, Graphic design scrolls left, off screen, and scrolling moves on to next section of the website. I achieved part of it; however, there is a weird jump as each stage scrolls off screen, and a long delay between Web Design scrolling off screen, and Graphic Design scrolling in. Below is my code. How can I make it not have such an empty gap? Things to note: 1. I am doing this in a Wordpress installation, using Elementor Pro plugin 2. I'm adding the GSAP as custom code under </body> 3. Looks very broken in CodePen
  6. Hello I have a problem with my scroll trigger animations. i want to make a pin panel animation that would stack my page_sections. and inside of each page section there will be scroll trigger with its own animation. i made this far that if in my Home component enable pin spacing on true the animations work but the panels are not stacking like if pinSpacing is false. But i cant figure out how to make it work that i would pin the element and make the animation like: hero anim -> 1 parallax section -> hotspot anim -> 2 parallax section -> and so on... now i am getting hero anim and 1 paralax section at the same time. and then hotspot anim and 2 paralax section at the same time. and because of that not all animations are seen.
  7. Hello. I need help with an implementation. All sections need to be pinned, and additionally, the first section should have pinSpacing for an extra animation, as shown in the GIF. My searches, unfortunately, have not been successful. Maybe there is a way to achieve this.
  8. Hi, When scrolling throw the container, they are being pinned and stacked on top of each other, but as soon as they are unpinned, the containers are transformed/moved out of place. The codepen showcases the issue I am facing. Thanks!
  9. NGrdanjski

    Words Carousel Animation

    Hi everyone! I need some help. I want to recreate the words-carousel section like on the site: https://c2montreal.com/ It's a section with three titles Learn, Experience, Connect. Section is full screen and on scroll have SplitText in some like roller/carousel effect. Here is my CodPen (scroll): https://codepen.io/NGrdanjski/pen/XJXwyOq Thank you very much everyone.
  10. Hi GSAP Community, I am currently facing an irresolvable issue with GSAP, which leads me to believe my approach is fundamentally flawed. The specific problem lies in the implementation of horizontal scrolling within my section2 element. When I attempt to pin this section to prevent its exit from the viewport, it causes the subsequent elements below to manifest prematurely. My objective is to have the viewport's scroll locked upon entering section2, thereby enabling a horizontal scroll through its internal elements. Once this internal scroll sequence is complete, the viewport's vertical scroll should resume, allowing the subsequent sections to enter the view in their natural order. Thanks in advance!
  11. Hey, I'm trying to apply a zoom masking effect where, as I scroll down, the text scales up. But when I scroll back up quickly, the masking doesn't align properly with the scroll speed.
  12. It doesn't work with a regular anchor, and if you use scrollTo, it doesn't scroll all the way to the end. Is there a way to make the scroll perfect to the desired element? Or at least within a couple dozen pixels?
  13. Hi, thank you for your help!. I'm struggling on only IOS. I tested on Macbook, Samsung, GooglePixel8, they got no issue at all except IOS (Iphones). . Behavior, I used everytime when `scrollTrigger.refresh()` got executed on IOS (Tested on Iphone11). I will have a mini-lag. So, I've tested on 9 years old Samsung and I got no issue at all. . So, everytime when I use scrollTrigger + scrub for increasing my element's width and height. The current width and height in scrollTrigger won't update their size (checked by markers). So, I've to use `scrollTrigger.refresh()` to fix this OR open dev tools to update the viewport again. It will get more worst when I need to use onUpdate + `scrollTrigger.refresh()` . This is how my code looks like (So, without onUpdate e.g. onStart or onComplate. They will also appear this bug as well only on IOS). if (window.innerHeight > window.innerWidth) { gsap.to(".aboutVideo", { scrollTrigger: { trigger: ".about__video", start: "bottom bottom", end: "center end", scrub: 0.5, }, onUpdate: () => { ScrollTrigger.refresh(); }, }); } I have the Netlify link for testing, but I would like to share it once when it’s done. I’d love for GSAP to consider it for a “Site of the Day” feature or something like that. I really want to show it to them, but I’m afraid that if I share it now, they might not consider it. Here is my link: https://everymatrix-porchelab.netlify.app/ ( I don't care anymore😞) My full code is here //Dependencies import gsap from "gsap"; import { useGSAP } from "@gsap/react"; import SplitText from "gsap/SplitText"; import { ScrollTrigger } from "gsap/ScrollTrigger"; import { TextPlugin } from "gsap/TextPlugin"; gsap.registerPlugin(ScrollTrigger, SplitText, TextPlugin); const InitLogic = () => { const historyTimeline = [ { title: "The Visionary Engineer", description: "“In 1931, Ferdinand Porsche founded a firm built on innovation and driven by passion.”", }, { title: "The First Porsche Legend", description: "“In 1948, Ferry Porsche built the 356, the first Porsche born from pure passion.”", }, { title: "The Designer of Legends", description: "“In 1963, Butzi Porsche shaped the 911, defining timeless performance.”", }, { title: "Porsche in the Spotlight", description: "“In 1970, Porsche ignited the world’s tracks; performance pulsed in its DNA.”", }, { title: "Breaking Boundaries", description: "“In 1986, the 959 fused technology and speed, redefining every limit.”", }, { title: "A New Era (2000s)", description: "“In 2002, Porsche entered new roads, blending sport and daily life seamlessly.”", }, ]; //===================================================================================== useGSAP(() => { //ScrollTrigger gsap.to(".homepageContent", { scrollTrigger: { trigger: ".homepageHero", start: "bottom 100%", end: "bottom 65%", scrub: 0.5, }, backgroundColor: "white", duration: 1, }); gsap.fromTo( ".section__about", { backgroundColor: "black", color: "white" }, { scrollTrigger: { trigger: ".homepageHero", start: "bottom 100%", end: "bottom 65%", scrub: 0.5, }, backgroundColor: "white", color: "black", duration: 1, } ); gsap.fromTo( ".homepageHero", { opacity: 1 }, { scrollTrigger: { trigger: ".homepageHero", start: "bottom 100%", end: "bottom 60%", scrub: 1, }, opacity: 0, filter: "blur(5px)", backgroundPositionY: window.innerWidth < 1024 ? "80%" : "300%", } ); gsap.fromTo( ".homepageHero__videoBG", { opacity: 1 }, { scrollTrigger: { trigger: ".homepageHero", start: "bottom 100%", end: "bottom 60%", scrub: 1, }, opacity: 0, filter: "blur(5px)", y: "-10%", } ); gsap.fromTo( ".aboutText1", { y: -700 }, { scrollTrigger: { trigger: ".homepageHero", start: "bottom 60%", end: "100% -30%", scrub: 0.4, }, y: 250, } ); gsap.fromTo( ".aboutText1", { opacity: 0 }, { scrollTrigger: { trigger: ".homepageHero", start: "bottom 60%", end: "100% top", scrub: 0.4, }, opacity: 1, } ); gsap.fromTo( ".aboutImage1", { y: -500, opacity: 0 }, { scrollTrigger: { trigger: ".homepageHero", start: "bottom 60%", end: "100% -30%", scrub: 0.4, }, y: 250, opacity: 1, } ); gsap.fromTo( ".innerAboutImage1", { width: "90%" }, { scrollTrigger: { trigger: ".homepageHero", start: "bottom 60%", end: "100% -30%", scrub: 0.4, }, width: "100%", } ); //Container2 gsap.fromTo( ".aboutContainer2", { y: 100, opacity: 0, zIndex: 1 }, { scrollTrigger: { trigger: ".aboutContainer2", start: "top 75%", end: "top center", scrub: 0.4, }, y: 150, opacity: 1, } ); gsap.fromTo( ".innerAboutImage2", { width: "95%" }, { scrollTrigger: { trigger: ".aboutContainer2", start: "top 75%", end: "top center", scrub: 0.4, }, width: "100%", } ); gsap.fromTo( ".aboutText2", { y: 150, opacity: 0 }, { scrollTrigger: { trigger: ".aboutContainer2", start: "top center", end: "bottom 50%", scrub: 0.4, }, y: 0, opacity: 1, } ); gsap.fromTo( ".aboutVideo", { y: 20 }, { scrollTrigger: { trigger: ".aboutVideo", start: "bottom bottom", end: "bottom top", scrub: 0.4, }, y: 50, } ); gsap.fromTo( ["body", ".section__about"], { backgroundColor: "white" }, { scrollTrigger: { trigger: ".section__about", start: "bottom top", end: "top bottom", scrub: 0.4, }, backgroundColor: "black", } ); gsap.fromTo( [".section__about", ".homepageContent"], { background: "white" }, { scrollTrigger: { trigger: ".about__video", start: "center bottom", end: "end end", scrub: 0.4, }, onStart: () => { ScrollTrigger.refresh(); }, backgroundColor: "black", } ); gsap.to(".aboutVideo", { scrollTrigger: { trigger: ".about__video", start: "bottom bottom", end: "end end", scrub: 0.4, }, width: "100%", height: "100vh", }); //Fixing bug if (window.innerHeight > window.innerWidth) { gsap.to(".aboutVideo", { scrollTrigger: { trigger: ".about__video", start: "bottom bottom", end: "center end", scrub: 0.5, }, onUpdate: () => { ScrollTrigger.refresh(); }, }); } // gsap.to(".innerAboutImage2", { scrollTrigger: { trigger: ".innerAboutImage2", start: "80% bottom", end: "bottom start", scrub: 0.4, }, translateY: "-15%", }); // loadingScreen gsap.to( [ ".loadingscreen__background--porsche", ".loading1", ".loading2", ".loading3", ".loading4", ], { x: "-100%", opacity: 0, duration: 0.3, delay: 0, ease: "power4.out", stagger: 0.1, onComplete: () => { gsap.to(".loadingscreen__container", { display: "none", }); }, } ); gsap.fromTo( ".nav__item.nav__item--logo", { opacity: 0.001, y: -60 }, { opacity: 1, y: -30, delay: 1, duration: 1, ease: "back.out", } ); gsap.fromTo( ".nav__item--hamburger", { opacity: 0.01 }, { opacity: 1, delay: 1.25, duration: 0.5 } ); gsap.fromTo( ".article__title", { opacity: 0.01, y: 20 }, { opacity: 1, y: 0, delay: 0.5, duration: 0.75 } ); gsap.fromTo( ".article__description", { opacity: 0.01, y: 20 }, { opacity: 1, y: 0, delay: 1, duration: 1 } ); gsap.to(".historySection__explaination", { opacity: 1, color: "#fff", scrollTrigger: { trigger: ".historySection__explaination", start: "center center", end: "5% top", scrub: 1, }, }); gsap.to(".historySection__explaination", { scrollTrigger: { trigger: ".historySection__explaination", start: "center center", end: "+=4000px top", scrub: 1, pin: true, }, }); historyTimeline.forEach((element, index) => { // animate image zoom & fade gsap.to(`.historyImage${index}`, { scrollTrigger: { trigger: `.historyImage${index}`, start: "top 70%", end: "bottom top", scrub: 0.4, }, onComplete: () => { ScrollTrigger.refresh(); }, opacity: 1, filter: "blur(0px)", width: "60%", }); // set initial title & description to first timeline gsap.set(".historySection__title", { text: historyTimeline[0].title, opacity: 1, }); gsap.set(".historySection__description", { text: historyTimeline[0].description, opacity: 1, }); // change text dynamically when each image becomes active ScrollTrigger.create({ trigger: `.historyImage${index}`, start: window.innerHeight > window.innerWidth ? "30% 70%" : "top 70%", end: window.innerHeight > window.innerWidth ? "bottom center" : "bottom top", scrub: 0.3, onEnterBack: index === 0 ? () => { gsap.to(".section__about", { backgroundColor: "#000", }); } : undefined, onToggle: (self) => { if (self.isActive) { gsap.to( [".historySection__title", ".historySection__description"], { opacity: 0, duration: 0.2, onComplete: () => { gsap.to(".historySection__title", { text: element.title, duration: 0.2, delay: 0, ease: "power2.out", opacity: 1, }); gsap.to(".historySection__description", { text: element.description, duration: 0.2, delay: 0, ease: "power2.in", opacity: 1, }); }, } ); } }, }); }); gsap.to(".historySection__historyContainer", { opacity: 0, duration: 1, ease: "power2.inOut", scrollTrigger: { trigger: ".historyImage5", start: "center center", end: "bottom 25%", scrub: 1, }, }); gsap.to(".historyPresent__presentEraImg", { opacity: 1, duration: 1, ease: "power2.inOut", scrollTrigger: { trigger: ".historyPresent__container", start: "top center", end: "top top", scrub: 1, }, }); gsap.to(".historyPresent__presentEraLogo", { filter: "brightness(0) invert(1) blur(0.5px)", opacity: 1, duration: 1, ease: "power2.inOut", scrollTrigger: { trigger: ".historyPresent__container", start: "top center", end: "top top", scrub: 1, }, }); gsap.to(".historyPresent__container", { scrollTrigger: { trigger: ".historyPresent__presentEraImg", start: "top top", end: "+=3000px bottom", scrub: 0.3, pin: true, }, }); gsap.to(".historyPresent__presentEraLogo", { filter: "brightness(0) invert(1) blur(0)", top: "10%", ease: "power4.in", scrollTrigger: { trigger: ".historyPresent__presentEraImg", start: "top top", end: "+=2000px bottom", scrub: 0.3, }, }); gsap.to(".historyPresent__presentEraChangingImg", { filter: "blur(0px)", ease: "power4.in", scrollTrigger: { trigger: ".historyPresent__presentEraImg", start: "top top", end: "+=2000px bottom", scrub: 0.3, }, }); gsap.to(".historyPresent__presentEraChangingImg", { opacity: 1, ease: "power4.in", scrollTrigger: { trigger: ".historyPresent__presentEraImg", start: "top top", end: "+=2000px bottom", scrub: 0.3, }, }); gsap.fromTo( ".historyPresent__presentEraMessage", { opacity: 0, y: -20 }, { filter: "blur(0px)", ease: "power4.in", opacity: 1, y: 0, scrollTrigger: { trigger: ".historyPresent__presentEraImg", start: "top top", end: "+=2000px bottom", scrub: 0.3, }, } ); gsap.to(".historyPresent__container", { opacity: 0, filter: "blur(5px)", scrollTrigger: { trigger: ".chooseCar__container", start: "top bottom", end: "top center", scrub: 0.3, }, }); gsap.to(".chooseCar__openingMessage", { color: "#fff", filter: "blur(0px)", scrollTrigger: { trigger: ".chooseCar__container", start: "top 60%", end: "top top", scrub: 0.3, }, }); gsap.to(".chooseCar__carList", { opacity: 1, filter: "blur(0px)", scrollTrigger: { trigger: ".chooseCar__openingMessage", start: "top 60%", end: "top top", scrub: 0.3, }, }); }, []); //History section useGSAP(() => { document.fonts.ready.then(() => { const split = new SplitText(".historySection__headLine", { type: "words", aria: "hidden", }); gsap.fromTo( split.words, { opacity: 0, y: 30 }, { opacity: 1, y: 0, ease: "none", stagger: 0.1, scrollTrigger: { trigger: ".historySection__headLine", start: "bottom bottom", end: "center 35%", scrub: 0.4, }, } ); gsap.to(".historySection__historyHR", { opacity: 1, y: 0, ease: "none", stagger: 0.1, scrollTrigger: { trigger: ".historySection__headLine", start: "600% bottom", end: "center 20%", scrub: 0.4, }, width: "100%", }); gsap.to(".about__video", { opacity: 0, scrollTrigger: { trigger: ".historySection__headLine", start: "bottom bottom", end: "top 25%", scrub: 0.4, }, onStart: () => { ScrollTrigger.refresh(); }, }); }); }, []); return <></>; }; export default InitLogic;
  14. Hello, just wanted to share that my new folio is now live at https://www.thomasthorstensson.com A big thanks to the team at GSAP for their work on ScrollTrigger and Draggable—two libraries that play a core part in this project. Anyone interested in how I implemented these, in a Nuxt SSR environment, follow the link on my folio to my GitHub. 😎 Thanks, .T
  15. hey there hope you guys are having a good day , i have a scenario which i should animate a element in while it has a scroll trigger and scrub lets say the loading state ends and then the box should animate from its original position to some where lets say { y : 0 } -> { y : -100}. the problem is i want this element to also have this animation with scroll trigger and scrub to true , i searched a lot for the possible ways but none of them worked smoothly until i found this and added a little code to control my animation with it but it might gets a little bit laggy if the user scrolls fast since the onUpdate on gsap tween which i wrote might doesn't run smoothly , does any one have any better solution to achieve my goal ?
  16. In short, I've employed ScrollSmoother and ScrollTrigger in my NextJS portfolio, but when I have Chrome/Edge devtools open and my app is refreshed (like when I've made a change in dev), the smooth scrolling stops. This wouldn't be the biggest deal if all that changed was the "lag" applied in scrolling, but other features that accompany SmoothScroller, like parallax and ScrollTrigger, begin to break down. Resizing the window given these conditions results in strange placement of any ScrollTriggers' markers. Moreover, I recently noticed that switching display orientation on mobile results in the same problem (just as is, not even in DevTools), if this helps diagnose my problem. It was one thing for it to affect my experience as a dev, but another now that I see the final product impacted, so any pointers would be greatly appreciated! The CodeSandbox demo I have linked below is a barebones React Project with the same component "SmoothScrollOverlay.tsx" that I use in my actual project. It's set up like this in my actual project because as mentioned, I'm using NextJS, so I separate GSAP from the page for SSR purposes. Do let me know if I'm abusing any bad practices in doing such. For context, I'm a student and new Software Engineer still building on my knowledge of React, let alone GSAP now, so if I've missed obvious things in the docs/forums, I apologize. It's been hard to combine my knowledge in both as I've struggled to find many resources that fit my goals, so if someone could push me in the right direction or link projects I could look at, I would greatly appreciate it! Thank you! https://codesandbox.io/p/sandbox/hjdqmr (code) https://hjdqmr.csb.app/ (replicate bug here. If needed, I can link a video of the bug as well)
  17. cR_BLue

    ScrollTrigger Animation Help Needed

    Hi, Im quite new to gsap, and wanted to create an animation that will show following animation i wanna create. 1)Basically it is a card stack type of animation 2)When each 「.mv-wrap」 reach their own respective「 strollTrigger : 'center center'」condition, 「.mv-overlay」will trigger and opacity change to 0.6. 3)Then 「.mv-wrap」 reach their own respective「 end」condition, 「.mv-blur」will trigger and 「filter: blur」 will apply effect to mv-blur. 4)Finally, text will scroll up until it reach the top its respectione 「.mv-wrap」top. I have been struggling for few days and yet cannot write the code i wanted to apply. Can someone help? *sry for my bad english https://codepen.io/Charles5BLueER/pen/QwyOYMg
  18. I’m seeing strange behavior in ScrollTrigger when I initialize the scroll triggers below the markers. I’ve created this simple demo to reproduce the issue I’m seeing in my larger project. Please click the "Scroll to Info" button first, then after scrolling, click the "Init" button to set up the scroll triggers. After initialization, slowly scroll up until you see the green box like in the image below. Notice that it is not at the correct position and when passing the orange marker it jumps to the end position and then plays in the correct way like it does when the initialization is done at the top of the page. Can anyone please explain why I am seeing this behavior and how to work around this.
  19. Building on top of my previous scroll-based carousel, I'm working to make the carousel draggable/scrollable on mobile devices. Currently I've tried two approaches: ScrollTrigger (Snap Property) scroll-snap demo Pros Already compatible with mobile scrolling Animations are spaced will between snap points Cons The scroll snapping doesn't centre the cards The snapping behaviour isn't "snappy" like native snapping. Even with the snap options (object). Scroll snap demo --- Drag + Inertia Plugin (Snap function) Pros Has great snapping Great inertia control Also works on mobile devices Cons Repositions (TranslateX) the element instead of scrolling it. This leaves sibling elements behind and follow the dragged item. When a container is dragged then scrolled (via arrow nav buttons), it goes way beyond the boundaries. Unknowns I'm not sure how to prevent or limit "throw dragging". When done on snapped drags, the script doesn't know which one to snap to. Dragging Demo --- I feel the ScrollTrigger method would be better but I'm not sure how to achieve what I aim to do.
  20. Following Snorkl.tv's 3D Flip tutorial, 3D flip tutorial I'm building a carousel that flips cards onto the front face when centred in the viewport. Essentially, I'm trying to get it to behave like Revolut's card switcher. In my demo, I can't seem to fine tune the spin animation of the cards based on the scroll distance. I've tried playing with the timeline repeat value + scrollTrigger's start and end values. In this instance, I discovered that matching the repeat to the number of cards almost makes it perfect. Each button click scrolls the slide container by 180px. Based on this scroll distance, I want the animation to reach it's halfway point (180º flip). Is this possible?
  21. Hi, pretty new to GSAP and Scrolltrigger but really want to get familiar with it and use it going forward. I'm trying to get spltjs text animating in on scroll with Scrolltrigger (Yes I know SplitText plugin exists but testing out external library functions with ST) but not having much luck. Codepen:
  22. Azzie Fuzzie

    ScrollTrigger pin not working on first load

    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" }); }); } }
  23. There's this extra space below the section even though i have given the section 680px of height.. and also at the end of this sectiona massive space is coming even though i never added it?.. heres the code "use client" import React, { useEffect, useRef } from 'react' import gsap from 'gsap' import { ScrollTrigger } from 'gsap/dist/ScrollTrigger' const steps = [ { id: 1, number: '1', title: 'Initial Discovery & Research', desc: 'We begin by aligning your goals with market research to validate and shape ideas.' }, { id: 2, number: '2', title: 'Brainstorming & Idea Generation', desc: 'We collaborate through brainstorming to spark innovation and explore diverse ideas.' }, { id: 3, number: '3', title: 'Idea Screening & Selection', desc: 'We filter ideas by feasibility, goals, and market fit to focus on those with the highest potential.' }, { id: 4, number: '4', title: 'User Persona Development', desc: 'We craft user personas to tailor concepts around real needs, behaviours, and pain points for user-centric solutions.' }, { id: 5, number: '5', title: 'Feasibility Study & Risk Assessment', desc: 'We assess feasibility and risks to build a strong, strategic foundation for product development.' }, { id: 6, number: '6', title: 'Prototyping & Concept Visualization', desc: 'We create prototypes and wireframes to visualize ideas, refine concepts, and gather stakeholder feedback.' }, { id: 7, number: '7', title: 'Feedback & Iteration', desc: 'We gather feedback to refine prototypes, ensuring concepts meet user needs and market expectations.' }, { id: 8, number: '8', title: 'Defining the Product Roadmap', desc: 'We design a product roadmap that defines timelines, milestones, and goals to guide development.' }, { id: 9, number: '9', title: 'Final Concept Approval', desc: 'The process ends with final concept approval, aligning all stakeholders for development.' } ] function ProductSuccess() { const sectionRef = useRef<HTMLDivElement | null>(null) const cardsContainerRef = useRef<HTMLDivElement | null>(null) const cardRefs = useRef<Array<HTMLDivElement | null>>([]) useEffect(() => { gsap.registerPlugin(ScrollTrigger) // Offset to leave space for fixed navbar (adjust if navbar height changes) const NAV_OFFSET_PX = -45 const section = sectionRef.current const cardsContainer = cardsContainerRef.current const cards = cardRefs.current if (!section || !cardsContainer || cards.length === 0) return // Calculate card height + gap const cardHeight = cards[0]?.offsetHeight || 0 const gap = 16 // mb-4 = 16px const totalCardHeight = cardHeight + gap // Set initial positions - all cards stacked below the viewport cards.forEach((card, index) => { if (index === 0) { gsap.set(card, { y: 0 }) } else { // Position each card below the previous one gsap.set(card, { y: totalCardHeight * index }) } }) // Create timeline for the animation const tl = gsap.timeline({ scrollTrigger: { trigger: section, // start when section hits just below navbar start: `top+=${NAV_OFFSET_PX} top`, end: () => `+=${totalCardHeight * (cards.length - 1) + window.innerHeight}`, scrub: 0.5, pin: true, anticipatePin: 1, } }) // Animate each card group cards.forEach((card, index) => { if (index > 0) { // Move all cards from current index onwards up by one card height const cardsToMove = cards.slice(index) tl.to(cardsToMove, { y: (cardIndex) => { const originalIndex = index + cardIndex const newPosition = totalCardHeight * (originalIndex - index) return newPosition }, duration: 1, ease: "none", stagger: 0 }, index === 1 ? 0 : ">") } }) return () => { ScrollTrigger.getAll().forEach(trigger => trigger.kill()) } }, []) return ( <section ref={sectionRef} className="w-full bg-white py-12 md:py-16 lg:py-20 h-[680px] overflow-hidden"> <div className="max-w-7xl mx-auto px-4 lg:px-0"> <div className="grid grid-cols-1 lg:grid-cols-2 gap-8 lg:gap-12 items-start"> {/* Left: Heading + paragraph (pinned) */} <div className="flex flex-col justify-start lg:sticky lg:top-0"> <h2 className="font-['Inter'] font-semibold text-[28px] md:text-[32px] lg:text-[36px] leading-[120%] text-[#000000] max-w-[550px]"> From Vision to Reality: Our Proven Path to Product Success </h2> <p className="mt-4 md:mt-8 font-['Inter'] font-normal text-[16px] leading-[24px] text-[#00000066] max-w-xl"> Our product ideation and conceptualization is a step-by-step journey from brainstorming and research to prototyping and refinement. We then craft a strategic roadmap to guide your concept into development, ensuring every decision aligns with your vision. </p> </div> {/* Right: Steps list with animation */} <div className="w-full relative"> <div ref={cardsContainerRef} className="relative h-[200px]"> <div className="absolute top-0 left-0 w-full"> {steps.map((s, index) => ( <div key={s.id} ref={el => { cardRefs.current[index] = el }} className="absolute top-0 left-0 w-full" > <div className="flex items-stretch gap-[36px]"> {/* Strip */} <div className="w-[66px] flex flex-col items-center flex-shrink-0 bg-white"> <div className="text-left whitespace-nowrap bg-white font-['Inter'] font-semibold text-[15px] leading-[20px] text-[#000000]"> {`STEP - 0${s.number}`} </div> <div className="flex-1 w-full flex items-start min-h-[120px]"> <div className="mx-auto w-0.5 h-full" style={{ background: 'repeating-linear-gradient(to bottom, #00000033 0px, #00000033 4px, transparent 4px, transparent 6px)' }} /> </div> </div> {/* Card */} <div className="flex-1 mb-4"> <div className="bg-[#FAFAFA] rounded-[12px] p-6"> <div className="flex flex-col items-start"> <div className="w-[48px] h-[48px] rounded-[8px] bg-[#3C82F6] flex items-center justify-center mb-3"> <span className="font-['Inter'] font-bold text-[24px] leading-[24px] text-white">{s.number}</span> </div> <div className="font-['Inter'] font-medium text-[18px] leading-[27px] text-[#0C0C0C]"> {s.title} </div> <div className="mt-1 font-['Inter'] font-normal text-[12px] leading-[20px] text-[#00000066]"> {s.desc} </div> </div> </div> </div> </div> </div> ))} </div> </div> </div> </div> </div> </section> ) } export default ProductSuccess
  24. Hi there! I'm a newbie - been struggling to get this working for a week now. I've created a function that animates the styling of the clicked nav-link. I also created a timeline with a scroll trigger that is supposed to call the same function when each section scrolls into view. (This is the part that's not working.) I think my mistake has something to do with scope? Sorry - I don't even exactly know what that means. The reason I think that, is because when I put the nav styling tweens directly into the Nav Animation timeline, then the animation works. If there is a more efficient way to achieve this please tell me. Thanks so much! Sonya. PS. I did manage to achieve the nav styling using scrollTrigger's toggleClass, but animating the style with css does not look as smooth.
  25. Guest

    Video Scroll Animation

    Where can I find a tutorial or maybe docs or blogs where I can learn how to make a video be animated on scroll. Like every time I scroll the video runs 1 or 2 frames. I am also interested in animating movement of elements on scroll. Let's say I have 3 card and as I scroll the care apea one by one when I scroll from a position of X= -100 to X= 0 while the hole scrolled is sopped. In other wards when the uses get to a point the normal scroll stops and as they scroll the card move from right to left raveling themself and moving into their position and ten the normal scrolling behavior start again. An example would be https://zoox.com/ . Thank you!
×
×
  • Create New...