Jump to content
Search Community

Search the Community

Showing results for tags 'react'.

  • 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)

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 238 results

  1. Hemananthan H P

    Cannot use RTG + GSAP

    Hi All, I am building simple website in react, while opening the navigation menu, GSAP animation not working for entering and exiting the Navigation menu component. I am providing the link below for a very very rough demo, I just need functionality for now. Thanks! I am using RTG for tracking the component Enter/Exit state, https://stackblitz.com/edit/vitejs-vite-iuxsfufp?file=src%2FApp.tsx
  2. 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.
  3. Hi, I used to be a proficient GSAP user until 2016, as a Creative Technologist in the Creative industry, Agencies, etc. Since 2015, I've been mainly working as a full-stack developer, interface or Frontend develop; and my focus and priorities have changed completely. Much has changed since, from Popmotion to Motion, GSAP becoming free? Etc. Although I worked with GSAP and similar libraries occasionally; I'm planning to pick GSAP again, as I'd like to focus more on design + engineering for some time. GSAP seem to have a good starting point at https://gsap.com/ui, which I'll use as a reference. If any of you have any good practices and conventions when working with React + Components + Vanilla CSS or Tailwindcss, is appreciated. Thank you!
  4. Hello, I’ve run into a MotionPathPlugin issue that I can’t seem to isolate. When I animate an SVG element (a droplet/rect) along a path using align: pathEl, the motion path itself renders fine, but the element always stays at the top-left corner of the SVG. When I enable MotionPathHelper.create(pathEl), the edit gizmo also appears at the top-left instead of over the actual dashed path. What’s confusing is that the path itself is visible and positioned correctly, but MotionPathPlugin seems to think the path is just a small curve near (0, 0). Here's what I'm doing (simplified for clarify): import gsap from "gsap"; import { useEffect, useRef } from "react"; import MotionPathPlugin from "gsap/MotionPathPlugin"; import { ScrollTrigger } from "gsap/ScrollTrigger"; import { MotionPathHelper } from "gsap/MotionPathHelper"; gsap.registerPlugin(MotionPathPlugin, ScrollTrigger); export default function TestMotion() { const pathRef = useRef(null); const dropletRef = useRef(null); useEffect(() => { const pathEl = pathRef.current; const droplet = dropletRef.current; gsap.set(droplet, { transformOrigin: "50% 50%", }); gsap.to(droplet, { motionPath: { path: pathEl, align: pathEl, alignOrigin: [0.5, 0.5], autoRotate: 270, }, ease: "none", scrollTrigger: { trigger: pathEl, start: "top center", scrub: true, }, }); MotionPathHelper.create(droplet); MotionPathHelper.create(pathEl); }, []); return ( <svg viewBox="0 0 1021 3018" width="100%" height="100%"> <path ref={pathRef} d="M100,100 C100,300 400,300 400,500" // example stroke="#97D5D0" fill="none" /> <rect ref={dropletRef} width="40" height="40" fill="#97D5D0" /> </svg> ); } What happens The dashed path renders correctly in the SVG. The blue helper overlay and the animated element both appear at the top-left corner. It looks like GSAP is aligning to some other invisible path (maybe a duplicate?). What I’ve checked Plugins are registered (MotionPathPlugin, ScrollTrigger) The refs are valid, not null pathEl.getBoundingClientRect() returns the expected dimensions I can provide a minimal CodePen demo if needed — just wanted to check if there’s an obvious mistake first. Thanks so much! 🙏 Environment: GSAP 3.13.0 React 19.1.0 @gsap/react 2.1.2 Zen (Firefox) | Macos
  5. Hello everyone! I have been trying to figure this out for so long I decided to ask for help. I'll try to explain the best I can, sorry in advance. As the title says, I want to create a fixed footer. I know that we can't use that with scrollSmoother at least not if they are in the same container. I found a suggestion somewhere here that does the trick but you can't interact with the footer content. My "solution" works well on desktop and footer is interactive, but on mobile the scrolling experience is really bad if not laggy and in some browsers (Opera GX) the footer height gets shifted. Having the margin there might be a bad practice but it kinda works. I'm at lost really i don't know how to figure this out while having interactive footer + smooth scrolling experience. Footer has stylings of absolute and using as height the svh. Thank you!
  6. Hey there, Trying to animated mapped components in my Next.js app. I have succeeded, but something is off.. The animation is not 'fluid'. See clip. I noticed it does not happen with 'a hardcoded array of items'. I also tried to grab all the buttons using gsap.utils.toArray(#button), but the same happens. How can I adjust the code for it to be fluent? Screen Recording 2025-09-18 at 11.55.28.mov Here is my code: const buttonRefs = useRef<HTMLAnchorElement[]>([]); buttonRefs.current = []; tl.fromTo( buttonRefs.current, { autoAlpha: 0, yPercent: 100 }, { autoAlpha: 1, yPercent: 0, ease: "linear", stagger: 0.1 } ); }, []); const addToRefs = (el: HTMLAnchorElement | null) => { if (el && !buttonRefs.current.includes(el)) { buttonRefs.current.push(el); } }; <div className="flex flex-row gap-4 w-fit"> {slice.primary.cta_buttons.map((item) => ( <Button ref={addToRefs} key={item.link.text} className="text-body-base font-medium" buttonStyle={item.style ?? "primary"} field={item.link} > {item.link.text} </Button> ))} </div> Much appreciated!
  7. Hello there, I'm trying to develop a portfolio thumbnail gallery that is horizontally scrollable and have parallax effect with gsap scrollTrigger and with skew applied for animating them based on vertical scroll speed, all this I'm trying to achieve with next.js 15 framework. Dev environment: Next.js 15, GSAP(ScrollTrigger, useGSAP), Lenis Problem: i'm unable the scroll till the last thumbnail. I'm only providing the code of the particular component file where I've implemented the gallery. 'use client'; import { useRef, useLayoutEffect } from 'react'; import { gsap } from 'gsap/dist/gsap'; import { ScrollTrigger } from 'gsap/dist/ScrollTrigger'; import { useGSAP } from '@gsap/react'; import Image from 'next/image'; console.clear(); gsap.registerPlugin(ScrollTrigger, useGSAP); const images = [ '/images/1.jpg', '/images/2.jpg', '/images/3.jpg', '/images/4.jpg', '/images/5.jpg', '/images/6.jpg', '/images/7.jpg', '/images/8.jpg', '/images/9.jpg', '/images/10.jpg', ]; const Gallery = () => { const containerRef = useRef(null); const galleryRef = useRef(null); useGSAP( () => { // Ensure gallery is not empty before calculating dimensions if (!containerRef.current || !galleryRef.current) return; const images = gsap.utils.toArray('.gallery-image-wrapper'); const gallery = galleryRef.current; const totalWidth = images.reduce( (sum, item) => sum + item.offsetWidth, 0, ); // We need to calculate how far to scroll the gallery const scrollDistance = totalWidth - window.innerWidth; // Pin the section and horizontally scroll the gallery wrapper const scrollTween = gsap.to(gallery, { x: -scrollDistance, // Animate to the negative scroll distance ease: 'none', scrollTrigger: { trigger: containerRef.current, pin: true, scrub: 2, start: 'top top', end: `+=${scrollDistance}`, // The end position matches the scroll distance invalidateOnRefresh: true, // Recalculates on resize markers: true, }, }); // quickSetter for optimized velocity skew const skewProxy = { skew: 0 }; const skewSetter = gsap.quickSetter( '.gallery-image-wrapper', 'skewX', 'deg', ); const clamp = gsap.utils.clamp(-10, 10); // Max skew amount in degrees ScrollTrigger.create({ onUpdate: (self) => { let skew = clamp(self.getVelocity() / -100); // Only update the skew if the absolute value is greater than the current one if (Math.abs(skew) > Math.abs(skewProxy.skew)) { skewProxy.skew = skew; gsap.to(skewProxy, { skew: 0, duration: 0.8, ease: 'power3.in', overwrite: true, onUpdate: () => skewSetter(skewProxy.skew), }); } }, }); // Parallax for individual images images.forEach((wrapper) => { const image = wrapper.querySelector('img'); gsap.to(image, { y: '20%', // Adjust the parallax intensity ease: 'none', scrollTrigger: { trigger: wrapper, containerAnimation: scrollTween, // Tie the animation to the horizontal scroll start: 'left right', end: 'right left', scrub: true, }, }); }); // Ensure GSAP recalculates on image load completion // You may need a more robust solution if using a complex image lazy-loading library const imagesLoadedPromise = new Promise((resolve) => { let loadedCount = 0; const totalImages = images.length; if (totalImages === 0) { resolve(); } images.forEach((imgWrapper) => { const img = imgWrapper.querySelector('img'); img.onload = () => { loadedCount++; if (loadedCount === totalImages) { resolve(); } }; }); }); imagesLoadedPromise.then(() => { ScrollTrigger.refresh(); }); }, { scope: containerRef, dependencies: [], revertOnUpdate: true }, ); return ( <section ref={containerRef} className='gallery-container'> <div ref={galleryRef} className='gallery-wrapper'> {images.map((src, index) => ( <div key={index} className='gallery-image-wrapper'> <div className='gallery-image-content'> <Image src={src} alt={`Gallery Image ${index + 1}`} width={400} height={500} className='gallery-image' priority={index === 0} // Optional: Prioritize the first image for faster loading /> </div> </div> ))} </div> </section> ); }; export default Gallery; I'm fetching this Gallery.js inside page.js in the app folder and usinng Lenis wrapper for react in the layout.js file. Please guide me with a prfoper solution.Gallery.js layout.js page.js LenisProvider.js
  8. Hi, Im new here! I just started to use GSAP in order to animate things in my website, but the thing is that I'm not sure what's the best way to make a complex button like for example this one, I know I have to use useGSAP, useRef and Timeline in order to make complex animations, But I'm not sure how to trigger mouseenter or mouseleave and how would look a button component with the best use guides for performance.
  9. Hey! I am Lucas, a full-stack dev that has been working with GSAP and 3D graphics for almost four years now. My portfolio was featured on the GSAP official showcase back in December 2023 and I also submitted my latest project. You can check it out first-hand here: https://gilneas-bank.lucaslamonier.com/ In this website you can travel to different universes to learn more about a currency bot for Discord. Beyond front end I also do: back end: Node.js, Next.js databases: MySQL, MongoDB, Google Clooud's Datastore hard surface modelling and basic animations in Blender Unreal Engine bots for Discord and more! If you have a cool idea or an interesting project going, reach out! Maybe we can do something awesome together! [email protected] https://www.lucaslamonier.com/
  10. I've managed to get video scrubbing working in a react project using the scroll trigger however once I pass the end the video snaps to first frame, and jumps back to the end if i scroll back up into the timeline is there a way to retain the value after the end position here is a video of what is currently happening https://streamable.com/sm47e4 here is a stripped down part of the code to only include relevant parts import gsap from "gsap"; import { ScrollTrigger } from "gsap/ScrollTrigger"; import { useGSAP } from '@gsap/react' gsap.registerPlugin(ScrollTrigger, useGSAP); const ScrollTimeline: React.FC<ScrollTimelineProps> = ({ }) => { const testRef = useRef(null); const elRef = useRef(null); const videoRef = useRef<VideoRef>(null); useGSAP(() => { const trigger = testRef.current; const tl = gsap.timeline({ defaults: { duration: 1 }, scrollTrigger: { trigger: trigger, start: "top top", pin: true, end: "+=600%", scrub: true } }); const el = elRef.current; tl .fromTo( el, { rotation: 0 }, { rotation: 180 } , 0 ) // FIX THIS, GET REF PROPERLY // This is to just get the video element after its properly rendered var interval if (!initialised) { setInitialised(true) interval = setInterval(() => { const videoEl = document.querySelector('video') if (videoEl) { clearInterval(interval); tl.fromTo( videoEl, { currentTime: 0 }, { currentTime: videoEl.duration, }, 0 ) } }, 2000) } }, []); return ( <div className={clsx(styles.page)}> <div className={clsx(styles.scrollTimeline)} ref={testRef}> {/* This element contains the video element */} <BackgroundMedia {...mediaArgs} /> <div className={clsx(styles.test)} ref={elRef}> TEST </div> </div> </div> ); }
  11. Hi GreenSock team 👋 I'm working with Next.js 15 and trying to orchestrate animations across multiple components using a shared masterTimelinethrough context. Each component defines its own gsap.timeline() and then adds it to the global timeline via masterTimeline.add(). For example: I have two components: Boxand Nav. - Box has its tl with 4 tweens and inserts a label after the second tween (addLabel('afterSecondTween')). - Nav defines its own timeline and tries to insert it at that label inside the master timeline using masterTimeline.add(navTL, 'afterSecondTween'). 🎯 My goal is to synchronize timelines between different components, so that they run in sequence or in specific positions. 💣 The result: Nav always animates at the start of the timeline. ✅ I’ve confirmed that: - The Nav timeline is added with the same label name. - But it still plays immediately (as if the label had time = 0). 💬 My question is: Is it correct to insert timelines at labels from other components in GSAP + React? Is this synchronization possible? What is the correct or recommended way to synchronize animations between different components in GSAP and React? Is this pattern okay: 1. Each component creates its own timeline 2. One of them defines a label at a specific point 3. Another component inserts its timeline at that label Or... is there a better / more robust way to orchestrate animations across components in React using GSAP? Thanks in advance for your insights 🙏 Here’s a minimal CodePen that replicates the problem: 📎 CodePen
  12. After reading react-advanced for several times, having chatgpt, deepseek and claude helping me to debug and trying my best I decide to create minimal demo to ask for help. I also did search forums but I didn't find clear solution for my problem that is not some kind of overengineering that I dont even understand. I created parent component (App.jsx) which creates the timeline. I created two child components, Heading.jsx and Loader.jsx and I pass them timeline with prop drilling (like gsap react-advanced article suggest). In each child, with useGSAP hook and timeline.add I add proper animations to timeline. I want Loader.jsx animation to run first and only then Heading.jsx. How do I do it? Obviously if I swap component order in DOM (in app.jsx) I can make Loader.jsx run first, but this will get messy in real project where I have more components and animations to add in timeline, and I also dont want to change my DOM order. I tried with labels but it didnt help me at all. I obviously dont want to use fixed seconds to decide what will run when, because real timeline is much more complex than these two components. I just want a way to control order of these animations, and let timeline handle when animation is finished and when next one will run. How do I control sequence of animations inside timeline that is created in parent component, and where child component add animations to parent timeline? Demo: https://stackblitz.com/edit/gsap-react-basic-f48716-kbuaghjm?file=src%2FApp.js,src%2Fcomponents%2FLoader%2FLoader.jsx,src%2Fcomponents%2FHeading%2FHeading.jsx Thank you
  13. LIR

    Animation Focus on ViewPort

    Hello, I hope you are well. First of all may I note that I am new using GSAP and that I am using it in React ("gsap" and "gsap/react" libraries). I want to reuse an animation, that basically is an horizontal scroll. Even though the animation works perfectly, I need the viewport to be centered on the whole parent section (so the title "Algunos proyectos_" and filter stays on sight). I tried setting the trigger by the section ID that is 'projects', by using document.querySelectorAll("sections.projects"), etc but always without the expected results. Can you guide me through or give me some assistance please? Thanks in advance! P.S.: This is how is seen while the animation takes place: P.S.2: This is how it should be seen while the animation takes place:
  14. I've been tinkering with fabric.js in a SharePoint React webpart today, which worked fine - allowing me to render shapes. I added gsap to test animating shapes, and for basic movement of shapes it worked straight away. However - if I use the "rotation" property, it immediately throws "Invalid property", and "Missing Plugin" errors. I've narrowed it down specifically to rotation - moving shapes around, and easing them seems to work perfectly. Here the core code of the component in the webpart: import * as React from 'react'; import type { IMySharePointMenuProps } from './IMySharePointMenuProps'; import * as fabric from 'fabric'; import { gsap } from 'gsap'; export default class MySharePointMenu extends React.Component<IMySharePointMenuProps> { private canvasRef = React.createRef<HTMLCanvasElement>(); private canvas: fabric.Canvas | null = null; public componentDidMount(): void { if (this.canvasRef.current) { // instantiate canvas this.canvas = new fabric.Canvas(this.canvasRef.current, { backgroundColor: 'lightgray' }); // create a rectangle const rect = new fabric.Rect({ left: 100, top: 100, width: 30, height: 30, fill: 'blue', angle: 0, hasControls: false }); // add the rectangle to the canvas this.canvas.add(rect); // render this.canvas.renderAll(); // add a click handler to the rectangle rect.on('mousedown', function(options){ // when clicked, animate the rectangle to a new location, and rotate it gsap.to(rect, { duration: 2, left: 200, top: 100, rotation: 45, ease: "power1.inOut", onUpdate: () => { if (this.canvas) { this.canvas.renderAll(); } }, onComplete: () => { // do something else } }); }); } } public render(): React.ReactElement<IMySharePointMenuProps> { return ( <div> <canvas ref={this.canvasRef} style={{ width: 500 , height: 500 }}></canvas> </div> ); } // Clean up the canvas object public componentWillUnmount(): void { if (this.canvas) { this.canvas.dispose(); this.canvas = null; } } } Any ideas what's causing it ?
  15. Hi! I hope you're doing great. I'm facing some issues with the code. First of all, I couldn't figure out how to use the pin feature because I've mostly used GSAP with plain HTML, CSS, and JavaScript. Secondly, I couldn't understand the strange behavior of the animation. When I enter the start marker, the content disappears. However, the animation triggered by ScrollTrigger runs smoothly when I exit and re-enter through the end marker. import React, { useEffect, useRef } from "react"; import { gsap } from "gsap"; import pakistanMap from "./assets/pakistanMap.svg"; import worldMap from "./assets/worldMap.svg"; import { ScrollTrigger } from "gsap/ScrollTrigger"; gsap.registerPlugin(ScrollTrigger); const Extra = () => { const worldRef = useRef(null); const pkRef = useRef(null); const containerRef = useRef(null); useEffect(() => { gsap.to(worldRef.current, { scale: 0.1, opacity: 0, duration: 4, scrollTrigger: { trigger: containerRef.current, start: "top 30%", end: "bottom 30%", markers: { start: "start", end: "end", }, scrub: 1, // pin: true, // pin: worldRef.current, // pin: containerRef.current, }, }); }, []); return ( <main className="w-full overflow-x-hidden"> <header className="w-screen h-[70vh] bg-gray-800"></header> <section ref={containerRef} className="container flex flex-col gap-4 items-center justify-center bg-white" > {/* World Map */} <div ref={worldRef} className=" w-[500px] h-auto bg-black/20 z-20"> <img className="w-full h-full object-contain" src={worldMap} alt="World Map" /> </div> {/* Pakistan Map */} <div ref={pkRef} className=" w-[500px] h-auto bg-green-100"> <img className="w-full h-full object-contain" src={pakistanMap} alt="Pakistan Map" /> </div> </section> <footer className="w-screen h-[80vh] bg-gray-800"></footer> </main> ); }; export default Extra;
  16. I am trying to build a portfolio with multiple pages. The very first page is a loader screen with some text. Right after the loader screen finishes, the landing page shows up. The issue is that the text animation of "JAVED RASIN" gets triggered and completed while the loader screen is being displayed. It's simply not waiting for the loader to finish and then start the animation. Here is the stackblitz project - https://stackblitz.com/edit/github-sq9mef9f?file=app%2Fpage.tsx
  17. Saeiko

    Alternate overlapping sections

    I've started using gsap about a month or so ago in a nextJs project. It is phenomenal and I'm really enjoying using it, but I have hit a wall when trying to make certain overlapping animations. Here's what I want: - I have 4 containers. A, B C and D. - step 1: Container A is pinned and some animations run inside it. - Step 2: When A's pin is done, B is pinned behind it and is revealed when scrolling down (A goes up and we find B pinned underneath). - When A is out of the view, do some animations inside B, when they're done B is unpinned and by scrolling down C is revealed (kinda like the opposite of step 1) - Step 3: repeat step 1 Each container is its own component. I have done some research and found some threads that suggest putting the containers in an gsap.utils.toArray but I didn't think that would give me the effect I want. Please correct me if I'm wrong. I have made a simple demo that's a bit close to what I want. In the demo A is pinned, but when the pin is done B pushes A up. I managed to make the 2nd step work (B is pinned and when it's unpinned C is revealed) I have been trying to solve this for about a week now without proper results, your help would be much appreciated.
  18. Initially, I wanted to create a GSAP ScrollTrigger responsive split-screen pinning effect similar to what is showcased on deveb.co. However, I was never able to get it to work. As a result, I decided to simplify and go back to the basics, using this codepen project: OLD scroll left pin right mockup -- not responsive. It was during this process that I realized the root of my issue had always been the same: whenever the column pinning starts, it disappears, and when the animation ends, it reappears. I've spent hours searching through forums for solutions but haven't found a fix. I first tried using gsap.context(); to resolve the issue: // Scroll.jsx import React, { useLayoutEffect } from 'react'; import { gsap } from 'gsap'; import { ScrollTrigger } from 'gsap/ScrollTrigger'; import './Scroll.css'; gsap.registerPlugin(ScrollTrigger); const Scroll = () => { useLayoutEffect(() => { let ctx = gsap.context(() => { ScrollTrigger.create({ trigger: ".gallery", start: "top top", end: "bottom bottom", pin: ".right", pinSpacing: true, markers: true, scrub: true, }); }); return () => ctx.revert(); }, []); return ( <> <div className="spacer"></div> <div className="gallery"> <div className="left"> <div className="detailsWrapper"> <div className="details"> <div className="headline"></div> <div className="text"></div> <div className="text"></div> <div className="text"></div> <div className="text"></div> </div> <div className="details"> <div className="headline"></div> <div className="text"></div> <div className="text"></div> <div className="text"></div> <div className="text"></div> </div> <div className="details"> <div className="headline"></div> <div className="text"></div> <div className="text"></div> <div className="text"></div> <div className="text"></div> </div> </div> </div> <div className="right"> <div className="photos"></div> </div> </div> <div className="spacer"></div> <div className="spacer"></div> <div className="spacer"></div> </> ); }; export default Scroll; That didn't work, so I decided to use useGSAP(), but it still produces the same result. // Scroll.jsx import React, { useRef } from 'react'; import { gsap } from 'gsap'; import { ScrollTrigger } from 'gsap/ScrollTrigger'; import { useGSAP } from '@gsap/react'; import './Scroll.css'; gsap.registerPlugin(ScrollTrigger); const Scroll = () => { const galleryRef = useRef(null); useGSAP( () => { ScrollTrigger.create({ trigger: galleryRef.current, start: "top top", end: "bottom bottom", pin: ".right", pinSpacing: true, scrub: true, markers: true, }); }, { scope: galleryRef } ); return ( <> <div className="spacer"></div> <div ref={galleryRef} className="gallery"> <div className="left"> <div className="detailsWrapper"> <div className="details"> <div className="headline"></div> <div className="text"></div> <div className="text"></div> <div className="text"></div> <div className="text"></div> </div> <div className="details"> <div className="headline"></div> <div className="text"></div> <div className="text"></div> <div className="text"></div> <div className="text"></div> </div> <div className="details"> <div className="headline"></div> <div className="text"></div> <div className="text"></div> <div className="text"></div> <div className="text"></div> </div> </div> </div> <div className="right"> <div className="photos"></div> </div> </div> <div className="spacer"></div> <div className="spacer"></div> <div className="spacer"></div> </> ); }; export default Scroll; I have reviewed the React & GSAP | GSAP | Docs & Learning, countless forums, and YouTube tutorials, but I’m still struggling to find a solution. I’m reaching out to you in the hope that you can help me. /* Scroll.css */ body { background: #EEF4FF; margin: 0; } .spacer { width: 100%; height: 50vh; background: #ddd; } .headline { background: #2D4E86; border-radius: 6px; height: 4em; width: 100%; } .text { margin: 0.8em 0 0 0; background: #2D4E86; border-radius: 6px; height: 1em; width: 100%; } .gallery { display: flex; outline: 1px solid red; height: auto; } .left { width: 50%; } .detailsWrapper { margin: auto; width: 80%; } .details { height: 100vh; outline: 1px solid green; display: flex; flex-direction: column; justify-content: center; } .right { position: sticky; top: 0; outline: 1px solid purple; width: 50%; height: 100vh; display: flex; flex-direction: column; justify-content: center; /* overflow: hidden; */ z-index: 1; } .photos { width: 40vw; height: 40vw; background: maroon; /* visibility: visible; */ } I’ve attached a video that visually demonstrates the issue, and I would be incredibly grateful for your assistance in resolving this. ScrollTrigger Pin Issue.mp4
  19. Hi there, I've been learning and really enjoying GSAP! Thanks for the wonderful docs, learning materials, and forum. I want to create an effect of an infinitely moving horizontal line, which moves FASTER or SLOWER in reaction to scroll. So passively, let's say the line scrolls at "duration 5". - If I scroll down, I want the line to move faster, e.g. at duration 5 - adjusted scroll velocity = 2. - If I scroll up, I want the line to move slower, e.g. at duration 5 + adjusted scroll velocity = 8. Here's a minimal demo of both effects individually, but I haven't been able to successfully combine them. I've tried to manually adjust the duration variable through ScrollTrigger's onUpdate, using "getVelocity()", but that didn't seem to work. I've left it blank in the demo. If someone could point me in the right direction of how to approach this, I can try to do it in the demo myself! Thanks again. (not sure how to embed stackblitz demo directly) https://stackblitz.com/edit/react-ejg7zb?embed=1&file=src%2FApp.js
  20. I 've been trying to solve this for days seeing the community and referring topics here in GSAP and tried various method but still could not solve this issue. As you can see from the code there are three div's and the second div has some content and I need to make one of the div's in it scroll horizontally while scrolling vertically. I have pinned the second div inorder to complete the horizontal scroll. It works sometime when I make corrections on the code and hit save. But when I refresh the page it stops working all the same. Also sometimes it works only when the cursor is on the left most side the horizontal scroll works. Any suggestions / help ? I have provided the codesandbox link for the same. https://codesandbox.io/p/sandbox/horizontal-scroll-q55rs3
  21. I have some simple sections being rendered in my React component but they are dynamically generated from a JSON file. All I need to do is pin each section to the top of the screen for an amount of time and then allow scrolling to the next section. I need to get the selector ID for each section and use that to identify the section as I scroll in and out. Do I need to create an animation for each section like this or can I use the class storymode-section and hit them all in one go? 'use client'; import React, { useEffect, useRef, useState } from 'react'; import { gsap } from "gsap"; import { useGSAP } from '@gsap/react'; import { ScrollTrigger } from "gsap/ScrollTrigger"; import { FakeStorymodeData } from '@/utils/FakeStorymode'; gsap.registerPlugin(useGSAP); gsap.registerPlugin(ScrollTrigger); interface StorymodeContentGSAPProps { } export const StorymodeContentGSAP: React.FC<StorymodeContentGSAPProps> = (props: StorymodeContentGSAPProps) => { const scrollRef = useRef<HTMLDivElement>(null); const [activeSection, setActiveSection] = useState<string>('Section 1'); const [selectors, setSelectors] = useState<string[]>([]); useGSAP(() => { // add a ScrollTrigger for each section that will pin it to the top of the viewport for the duration of the viewport selectors.forEach((selector) => { const sectionElement = scrollRef.current?.querySelector(`#${selector}`); if (!sectionElement) return; gsap.to(sectionElement, { scrollTrigger: { trigger: sectionElement, start: "top top", end: "bottom top", pin: true, pinSpacing: false, onEnter: () => setActiveSection(selector), onLeaveBack: () => setActiveSection(selector), } }); } ); }, {dependencies: [selectors], scope: scrollRef}); useEffect(() => { // create an array of the section.id's const sectionIds = FakeStorymodeData.map((section) => section.id); setSelectors(sectionIds); }, []); return ( <div ref={scrollRef} className="w-full h-full flex flex-col justify-start flex-shrink-0 overflow-y-auto prose "> { FakeStorymodeData.map((section, index) => { return ( <section id={section.id} key={index} className={"storymode-section flex flex-col justify-start w-full p-2 mb-64 " }> <header className="w-full bg-gray-300 p-2 rounded"><strong>STICKY HEADER</strong></header> <h2>{section.header}</h2> <p>{section.content}</p> </section> ); }) } </div> ); }
  22. Hello, first of all I apologize if my English is not very good. I don't write the language fluently This is my first post and I'm new to GSAP. Believe me, I didn't want to post this without trying to solve the animation problem first. I have an animation where, at the starting point, the cards appear stacked one below the other. As you scroll, each card moves up and eventually exits the screen, while the bottom card adjusts to position y:0. The problem arises when the animation is reversed. As shown in the preview, the cards do not return to their correct positions and the animation does not complete as expected. I hope someone can help me with this. Thank you! PD: Reload the page if the animation doesn't work as expected, I don't know what's going on. After reloading, it works fine. PREVIEW: https://stackblitz.com/edit/gsap-react-basic-f48716-vwwr1u?file=src%2FApp.js
  23. Hello everyone, I am trying to create a slideshow based on the demo found at the end of this post. https://codepen.io/cassie-codes/pen/vYWvwXV Instead, my version uses React to try to recreate the same effect. However I am running into problems. Here is my code: slides.jsx import { useEffect, useRef } from "react"; import gsap from "gsap"; import { ScrollTrigger } from "gsap/ScrollTrigger"; import { Observer } from "gsap/Observer"; import { projects } from "./projects"; import "./slideshow.scss"; gsap.registerPlugin(ScrollTrigger, Observer); const SlideShow = () => { const sectionsRef = useRef([]); const imagesRef = useRef([]); const slideImagesRef = useRef([]); const outerWrappersRef = useRef([]); const innerWrappersRef = useRef([]); const countRef = useRef(null); const currentIndex = useRef(0); const animating = useRef(false); useEffect(() => { const sections = sectionsRef.current; const images = imagesRef.current.reverse(); const slideImages = slideImagesRef.current; const outerWrappers = outerWrappersRef.current; const innerWrappers = innerWrappersRef.current; const count = countRef.current; const wrap = gsap.utils.wrap(0, sections.length); gsap.set(outerWrappers, { xPercent: 100 }); gsap.set(innerWrappers, { xPercent: -100 }); gsap.set(sections[0].querySelector(".slide__outer"), { xPercent: 0 }); gsap.set(sections[0].querySelector(".slide__inner"), { xPercent: 0 }); function gotoSection(index, direction) { animating.current = true; index = wrap(index); let tl = gsap.timeline({ defaults: { duration: 1, ease: "expo.inOut" }, onComplete: () => { animating.current = false; console.log("slide changed"); }, }); const currentSection = sections[currentIndex.current]; const heading = currentSection.querySelector(".slide__heading"); const nextSection = sections[index]; const nextHeading = nextSection.querySelector(".slide__heading"); gsap.set([sections, images], { zIndex: 0, autoAlpha: 0 }); gsap.set([sections[currentIndex.current], images[index]], { zIndex: 1, autoAlpha: 1, }); gsap.set([sections[index], images[currentIndex.current]], { zIndex: 2, autoAlpha: 1, }); tl.set(count, { text: index + 1 }, 0.32) .fromTo( outerWrappers[index], { xPercent: 100 * direction }, { xPercent: 0 }, 0 ) .fromTo( innerWrappers[index], { xPercent: -100 * direction }, { xPercent: 0 }, 0 ) .to(heading, { "--width": 800, xPercent: 30 * direction }, 0) .fromTo( nextHeading, { "--width": 800, xPercent: -30 * direction }, { "--width": 200, xPercent: 0 }, 0 ) .fromTo( images[index], { xPercent: 125 * direction, scaleX: 1.5, scaleY: 1.3 }, { xPercent: 0, scaleX: 1, scaleY: 1, duration: 1 }, 0 ) .fromTo( images[currentIndex.current], { xPercent: 0, scaleX: 1, scaleY: 1 }, { xPercent: -125 * direction, scaleX: 1.5, scaleY: 1.3 }, 0 ) .fromTo(slideImages[index], { scale: 2 }, { scale: 1 }, 0) .timeScale(0.8); currentIndex.current = index; } Observer.create({ type: "wheel,touch,pointer", preventDefault: true, wheelSpeed: -1, onUp: () => { if (animating.current) return; gotoSection(currentIndex.current + 1, 1); }, onDown: () => { if (animating.current) return; gotoSection(currentIndex.current - 1, -1); }, tolerance: 10, }); const logKey = (e) => { if ( (e.code === "ArrowUp" || e.code === "ArrowLeft") && !animating.current ) { gotoSection(currentIndex.current - 1, -1); } if ( (e.code === "ArrowDown" || e.code === "ArrowRight" || e.code === "Space" || e.code === "Enter") && !animating.current ) { gotoSection(currentIndex.current + 1, 1); } }; document.addEventListener("keydown", logKey); return () => { document.removeEventListener("keydown", logKey); }; }, []); return ( <div className="slideshow_container"> {projects.map((project, i) => ( <section className="slide" key={project.id} ref={(el) => (sectionsRef.current[i] = el)} > <div className="slide__outer" ref={(el) => (outerWrappersRef.current[i] = el)} > <div className="slide__inner" ref={(el) => (innerWrappersRef.current[i] = el)} > <div className="slide__content"> <div className="slide__container"> <h2 className="slide__heading">{project.projName}</h2> <figure className="slide__img-cont" ref={(el) => (slideImagesRef.current[i] = el)} > <img src={project.codeImgSrc} alt={project.codeImgAlt} /> </figure> </div> </div> </div> </div> <div className="overlay"> <div className="overlay__content"> <p className="overlay__count">{i + 1}</p> <figure className="overlay__img-cont" ref={(el) => (imagesRef.current[i] = el)} > <img src={project.demoImgSrc} alt={project.demoImgAlt} /> </figure> </div> </div> </section> ))} <div className="link_buttons"> <button>Code</button> <button>Demo</button> </div> </div> ); }; export default SlideShow; slideshow.scss I want this slideshow to take up only half the screen @font-face { font-family: "Bandeins Sans & Strange Variable"; src: url("https://res.cloudinary.com/dldmpwpcp/raw/upload/v1566406079/BandeinsStrangeVariable_esetvq.ttf"); } @import url("https://fonts.googleapis.com/css2?family=Sora&display=swap"); * { box-sizing: border-box; user-select: none; } ::-webkit-scrollbar { display: none; } figure { margin: 0; overflow: hidden; } html, body { overflow: hidden; margin: 0; padding: 0; height: 100vh; height: -webkit-fill-available; } .slide { height: 100%; width: 100%; top: 0; left: 50%; position: fixed; visibility: hidden; &__outer, &__inner { width: 100%; height: 100%; overflow-y: hidden; } &__content { display: flex; align-items: center; justify-content: center; position: absolute; height: 100%; width: 100%; top: 0; } &__container { position: relative; max-width: 1400px; width: 100vw; margin: 0 auto; height: 90vh; margin-bottom: 10vh; display: grid; grid-template-columns: repeat(10, 1fr); grid-template-rows: repeat(10, 1fr); grid-column-gap: 0px; grid-row-gap: 0px; padding: 0 1rem; } &__heading { --width: 200; display: block; text-align: left; font-family: "Bandeins Sans & Strange Variable"; font-size: clamp(5rem, 5vw, 5rem); font-weight: 900; font-variation-settings: "wdth" var(--width); margin: 0; padding: 0; color: #f2f1fc; z-index: 999; mix-blend-mode: difference; grid-area: 2 / 2 / 3 / 10; align-self: baseline; } &__img-cont { margin-top: 4rem; grid-area: 2 / 1 / 7 / 8; img { width: 100%; height: 100%; object-fit: cover; } } } .slide:nth-of-type(1) { visibility: visible; .slide__content { backdrop-filter: blur(10px); } } .slide:nth-of-type(2) { .slide__content { backdrop-filter: blur(10px); } } .slide:nth-of-type(3) { .slide__content { backdrop-filter: blur(10px); } } .slide:nth-of-type(4) { .slide__content { backdrop-filter: blur(10px); } } .overlay { position: fixed; top: 0; bottom: 0; left: 50%; right: 0; z-index: 2; &__content { max-width: 1400px; width: 100vw; margin: 0 auto; padding: 0 1rem; height: 90vh; margin-bottom: 10vh; display: grid; grid-template-columns: repeat(10, 1fr); grid-template-rows: repeat(10, 1fr); grid-column-gap: 0px; grid-row-gap: 0px; } &__img-cont { position: relative; overflow: hidden; margin: 0; grid-area: 4 / 3 / 9 / 5; img { position: absolute; width: 100%; height: 100%; object-fit: cover; object-position: 50% 50%; } } &__count { grid-area: 3 / 5 / 4 / 5; font-family: "Bandeins Sans & Strange Variable"; font-size: clamp(3rem, 4vw, 15rem); margin: 0; padding: 0; text-align: right; border-bottom: 7px white solid; } } @media screen and (min-width: 900px) { .overlay__content, .slide__container { padding: 0 3rem; margin-top: 10vh; height: 80vh; } .overlay__img-cont { grid-area: 6 / 2 / 10 / 6; } .overlay__count { grid-area: 3 / 5 / 4 / 5; font-family: "Bandeins Sans & Strange Variable"; color: white; } .slide__img-cont { margin-top: 0; grid-area: 2 / 1 / 8 / 7; } .slide__heading { grid-area: 1 / 1 / 4 / 10; } } projects.js export const projects = [ { id: 1, projName: "test1", codeLink: "(some link)", demoLink: "(some other link)", codeImgSrc: "(some picture src)", codeImgAlt: "", demoImgSrc: "(some other picture src)", demoImgAlt: "", }, { id: 2, projName: "test2", codeLink: "(some link)", demoLink: "(some other link)", codeImgSrc: "(some picture src)", codeImgAlt: "", demoImgSrc: "(some other picture src)", demoImgAlt: "", }, { id: 3, projName: "test3", codeLink: "(some link)", demoLink: "(some other link)", codeImgSrc: "(some picture src)", codeImgAlt: "", demoImgSrc: "(some other picture src)", demoImgAlt: "", }, { id: 4, projName: "test4", codeLink: "(some link)", demoLink: "(some other link)", codeImgSrc: "(some picture src)", codeImgAlt: "", demoImgSrc: "(some other picture src)", demoImgAlt: "", }, ]; My major issue right now is that I cannot get the overflow: hidden; to apply correctly causing the old text and pictures to remain on the screen instead of being hidden. Also when the next slide animates, the current and next numbers on the counter render at the same time, overlapping for a moment. If anyone can help it would be appreciated. Thanks in advance!
  24. const DotCursor: React.FC = () => { const dotRef = useRef<HTMLDivElement>(null); const { clientX, clientY } = useMousePosition(); const [isVisible, setIsVisible] = useState(false); const handleMouseEnter = () => { setIsVisible(true); }; const handleMouseLeave = () => { setIsVisible(false); }; const handleMouseMove = () => { setIsVisible(true); }; useGSAP( () => { const dot = dotRef.current; if (!dot) return; window.addEventListener("mousemove", handleMouseMove); document.addEventListener("mouseenter", handleMouseEnter); document.addEventListener("mouseleave", handleMouseLeave); const animateDot = () => { gsap.set(dot, { x: clientX, y: clientY, }); }; gsap.ticker.add(animateDot); return () => { window.removeEventListener("mousemove", handleMouseMove); document.removeEventListener("mouseenter", handleMouseEnter); document.removeEventListener("mouseleave", handleMouseLeave); gsap.ticker.remove(animateDot); }; }, { dependencies: [clientX, clientY] } ); return ( <div ref={dotRef} className="dot-cursor bg-orange-700" style={{ opacity: isVisible ? 1 : 0, position: "fixed", width: "10px", height: "10px", border: "solid 1px black", borderRadius: "20%", pointerEvents: "none", transform: "translate(-50%, -50%)", zIndex: 9999, }} /> ); }; export default DotCursor;
  25. Oliver Boucher

    Connection problem with header when scrolling

    Hi, I'm making project with React and GSSP. I want to make header effect that connect with scrolling. This is my project with codesandbox. https://codesandbox.io/p/devbox/cocky-ben-fgwh9p?file=%2Fsrc%2Futils%2FdataUtils.tsx%3A11%2C29 In my project I have got issue when click the navbar. When I click the navbar link, the navbar animation replay.
×
×
  • Create New...