Search the Community
Showing results for tags 'react'.
-
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
-
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:
- 5 replies
-
- scrolltrigger
- viewport
-
(and 3 more)
Tagged with:
-
Problems with rotation when using gsap with fabric.js in a SharePoint React WebPart
Jonathan Beckett posted a topic in GSAP
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 ?- 4 replies
-
- sharepoint
- webpart
-
(and 5 more)
Tagged with:
-
ScrollTrigger Position Issues After Route Change in React App
moostakimahamed posted a topic in GSAP
I use GSAP’s ScrollTrigger in my React app, and everything works perfectly in the local environment. However, after deploying to Vercel, I’ve encountered an issue with ScrollTrigger when switching routes. Here’s the behavior: When I navigate to a page, the ScrollTrigger positions (start and end markers) are not aligned with the actual content. For example, the start and end markers appear above the intended elements. (I noticed it in all my sections) If I refresh the page, the ScrollTrigger recalculates correctly, and everything works perfectly. Navigating to another route causes the same issue to arise until I refresh the page again. I’ve also noticed that I’m using the Swiper component in two routes at the bottom of the page, reusing the same component. This seems to cause similar issues even in my development server. (I have properly used context for this component) [Note: I have added lazy loading for components and don't have any lazy loading images] const Home = lazy(() => import("@/pages/Home/Home")); Here is a minimal useGsap code from my code: useGSAP(() => { const cards = gsap.utils.toArray( ".card" ) as HTMLElement[]; cards.forEach((item, i) => { gsap.from(item, { y: 50, duration: 1, opacity: 0, delay: i * 0.1, ease: "power2.out", scrollTrigger: { trigger: item, start: "top 90%", toggleActions: "play none none none", markers: true, }, }); }); }, []); Any advice on how to ensure ScrollTrigger recalculates properly? And the best practices to follow when using GSAP with React or Next.js -
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;
- 4 replies
-
- react
- tailwindcss
-
(and 2 more)
Tagged with:
-
I don't know why this isn't working as intended function MoviesList({ movies, onSelectMovie }) { const moviesList = useRef(null); useGSAP( () => { if (!movies.length) return; gsap.from(moviesList.current.querySelectorAll("li"), { opacity: 0, x: 120, // If you want movement, otherwise remove this line stagger: 0.2, duration: 0.5, ease: "power2.out", }); }, { scope: moviesList, dependencies: [movies] } ); return ( <ul className="list list-movies" ref={moviesList} > {movies?.map((movie) => ( <Movie movie={movie} key={movie.imdbID} onSelectMovie={onSelectMovie} ></Movie> ))} </ul> ); } function Movie({ movie, onSelectMovie }) { return ( <li onClick={() => onSelectMovie(movie.imdbID)}> <img src={movie.Poster} alt={`${movie.Title} poster`} /> <h3>{movie.Title}</h3> <div> <p> <span>🗓</span> <span>{movie.Year}</span> </p> </div> </li> ); } but this one which is exactly the same is working perfectly function WatchedMoviesList({ watched, onDeleteWatchedMovie }) { const watchedContainerRef = useRef(null); useGSAP( () => { if (!watched.length) return; gsap.from(".watched-list li", { opacity: 0, x: 120, stagger: 0.2, duration: 0.5, ease: "power2.out", }); }, { scope: watchedContainerRef } ); return ( <ul className="list watched-list" ref={watchedContainerRef} > {watched.map((movie) => ( <WatchedMovie movie={movie} onDeleteWatchedMovie={onDeleteWatchedMovie} key={movie.imdbID} /> ))} </ul> ); } function WatchedMovie({ movie, onDeleteWatchedMovie }) { return ( <li> <img src={movie.poster} alt={`${movie.title} poster`} /> <h3>{movie.title}</h3> <div> <p> <span>⭐️</span> <span>{movie.imdbRating}</span> </p> <p> <span>🌟</span> <span>{movie.userRating}</span> </p> <p> <span>⏳</span> <span>{movie.runtime}</span> </p> <button className="btn-delete" onClick={() => onDeleteWatchedMovie(movie.imdbID)} > x </button> </div> </li> ); } This is the issue I'm facing that the animation left isn't working properly and I can't figure it out
-
hey that is a SOS call I going to develop a commecrial website in react, and need drawSvgPlugin. currently i make an sample little project in stackBlitz, and it is not recognize the llugin from the gsap lib - only from the gsap-trial lib I saw in pricing that the drawSvg is free use feature - even for commecrial websites. if i took it from gsap-trial - does it mean i have to pay on it? something n0t clear to me help please 🙏!! thanks
-
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
-
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.
- 4 replies
-
- scrolltrigger
- pin
-
(and 2 more)
Tagged with:
-
It has to look so: but the most I manage to achive is: I don't know how to make the second side of the V shape, I will be thirled for help by fixing my code or simply give me a new match code. thanks in advanced. attached is the current react code I'm using: css: * { box-sizing: border-box; } html, body { height: 100%; min-height: 100%; font-family: Roboto, sans-serif; background: #e2e0e0; } main { display: flex; position: relative; flex-direction: column; width: 100vw; height: 100vh; overflow: hidden; align-items: center; justify-content: center; } .cell { position: absolute; top: 0; left: 0; width: 100%; font-size: 26px; font-weight: 500; color: rgba(0, 0, 0, 0.87); user-select: all; display: flex; align-items: center; justify-content: center; background: #eee; height: 70%; transform-origin: center center; } .picker { position: relative; overflow: hidden; width: 80vw; height: 90vh; border-radius: 2px; background-color: #fff; box-shadow: 0 2px 4px -1px rgba(0, 0, 0, 0.2), 0 4px 5px 0 rgba(0, 0, 0, 0.14), 0 1px 10px 0 rgba(0, 0, 0, 0.12); } js: import { useRef } from 'react'; import gsap from 'gsap'; import { Draggable } from 'gsap/all'; import { useGSAP } from '@gsap/react'; gsap.registerPlugin(Draggable); const App = () => { const pickerRef = useRef(null); const cellsRef = useRef([]); useGSAP(() => { gsap.defaults({ ease: 'none' }); const picker = pickerRef.current; const cells = cellsRef.current; const proxy = document.createElement('div'); const myWrapper = gsap.utils.wrap(0, 0.871); const cellWidth = 75; const numCells = cells.length; const cellStep = 1 / numCells; const wrapWidth = cellWidth * numCells; const baseTl = gsap.timeline({ paused: true }); gsap.set(picker, { width: wrapWidth - cellWidth, }); cells.forEach((cell, i) => { initCell(cell, i); }); const animation = gsap .timeline({ repeat: -1, paused: true }) .add(baseTl.tweenFromTo(1, 2)); const draggable = Draggable.create(proxy, { type: 'x', trigger: picker, inertia: true, onDrag: updateProgress, onThrowUpdate: updateProgress, snap: { x: snapX, }, onThrowComplete: () => { console.log('onThrowComplete'); // TODO: animation that injects selected card title }, })[0]; function snapX(x) { return Math.round(x / cellWidth) * cellWidth; } function updateProgress() { animation.progress(myWrapper(draggable.x / wrapWidth)); } function initCell(element, index) { gsap.set(element, { width: cellWidth, scale: 0.6, x: -cellWidth, y: 0, }); const tl = gsap .timeline({ repeat: 1 }) .to(element, { duration: 1, x: `+=${wrapWidth}`, y: '+=70' }, 0) .to( element, { duration: cellStep, color: '#009688', scale: 0.778, repeat: 1, yoyo: true, }, 0.5 - cellStep ); baseTl.add(tl, index * -cellStep); } }, []); return ( <main> <div ref={pickerRef} className="picker"> {Array.from({ length: 5 }, (_, index) => ( <div key={index} ref={(el) => (cellsRef.current[index] = el)} className="cell" > <div className="cell-content">{`Card ${index + 1}`}</div> </div> ))} </div> </main> ); }; export default App;
-
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
- 3 replies
-
- scrolltrigger
- react
-
(and 3 more)
Tagged with:
-
Hi, guyz I am Nadeem I am running into error that my svg is not actually morphing . Can anyone help me with this ....
-
Hey, everybody! I want to ask you. How can I work with elements with GSAP that come with dangerouslySetInnerHtml, because during animation inline styles are not written in them, although I can see through devtools that such an attempt is made (elements are highlighted, but nothing appears in them)
-
How can ScrollTrigger affect speed of existing infinite animation? (w/ React demo)
LevFokichev posted a topic in GSAP
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 -
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
- 2 replies
-
- horizontal scroll
- scroll trigger
-
(and 1 more)
Tagged with:
-
Adding ScrollTrigger animations to dynamically generated React markup
Lee Probert posted a topic in GSAP
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> ); } -
Hello! I Next.js I got a SectionHeadline component that I need to split into lines to apply animations. The problem is that when I apply SplitText plugin (like I do below) it messes with all of my nested components (it includes things lik WordsLoop slider that has some ScrollTriggers within. All of those scrolltriggers break and nothing works like expected. I wonder what am I doing wrong. The line splitting code (adding divs for each line works), but interestingly on autoreload it gives an error: NotFoundError: Failed to execute 'removeChild' on 'Node': The node to be removed is not a child of this node. When I refresh the page, it's fine. What am I doing wrong? Thank you! "use client"; import { useGSAP } from "@gsap/react"; import gsap from "gsap"; import { SplitText } from "gsap/SplitText"; import type { PortableTextProps } from "next-sanity"; import { PortableText } from "next-sanity"; import { useRef } from "react"; import { WordsLoop } from "~/components/words-loop"; import { cx } from "~/lib/utils/styled"; export function SectionHeadline({ value, className, ...rest }: Pick<PortableTextProps, "value"> & React.ComponentPropsWithoutRef<"div">) { const headlineRef = useRef<HTMLDivElement>(null); useGSAP( () => { gsap.registerPlugin(SplitText); const split = new SplitText("p", { type: "lines" }); }, { scope: headlineRef } ); return ( <div ref={headlineRef} className={cx("block px-32 text-center font-serif text-title-90", className)} {...rest} > <PortableText value={value} components={{ marks: { strong: (p) => { return <strong className="font-heading text-title-70">{p.children}</strong>; }, em: (p) => { return <em className="italic">{p.children}</em>; }, wordsLoop: (p) => { if (!p.value.words) { return <span className="font-heading text-title-70">{p.children}</span>; } return <WordsLoop words={p.value.words} />; }, }, }} /> </div> ); }
-
ScrollTrigger Horizontall Scrolll Reset first position again when Upward scrolling in Next Js
kiran022 posted a topic in GSAP
useEffect(() => { gsap.registerPlugin(ScrollTrigger, ScrollToPlugin); const section: any = scrollRef.current; const scrollContainer = section.querySelector(".scroll-container"); const scrubTween= gsap.to(scrollContainer, { x: () => -(scrollContainer.scrollWidth - section.offsetWidth + 104) + "px", ease: "none", scrollTrigger: { trigger: section, start: "center center", end: () => "+=" + (scrollContainer.scrollWidth - section.offsetWidth), pin: true, anticipatePin: 1, invalidateOnRefresh: true, scrub:0, onUpdate: (self) => { if (self.direction === -1) { // Scrolling upwards gsap.set(scrollContainer, { x: 0 }); // Reset horizontal scroll position } }, }, }); return () => { ScrollTrigger.getAll().forEach((trigger) => trigger.kill()); }; }, []) -
I've been trying all night to install and import Split text on react, I've tried various ways to import the plugin and this is my current code. import React, {useRef} from 'react' import { useGSAP } from '@gsap/react' import gsap from 'gsap' import { SplitText } from "gsap/SplitText" gsap.registerPlugin(SplitText) Here's the error message I keep getting: Internal server error: Failed to resolve import "gsap/SplitText" from "src/components/Onboarding.jsx". Does the file exist? On further investigation in the node_modules/gsap directory, I realized the SplitText.js file actually doesn't exist, has this feature been deprecated or am I just installing it wrong?
-
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
-
When i enable pin on my text reveal, it just jumps to the bottom
hdjdgskshdjdnfkshshd posted a topic in GSAP
Hello there, at first it works good, but after i enable pin it just jumps to the bottom and only works when scrolling back up, how can i pin it the same position and after the revealing is done, and then i can scroll pass that, thank you in advance, i attached a video (sorry for the compression, 1mb limit) const About = () => { gsap.registerPlugin(useGSAP,ScrollTrigger); const text = new SplitType('#target') gsap.from(text.chars, { scrollTrigger: { trigger: "#target", start: 'top 70%', end: 'top 00%', scrub: true, pinSpacing: false, markers: false }, opacity: 0.1, y: 20, stagger: 0.01 }) return ( <div className='about content-center items-center text-center overflow-hidden py-12 p-5 lg:px-72'> <p id='target' className='lg:text-5xl text-3xl font-bold text-neutral-200'> I’m Dilukshan, a fullstack web developer from Sri Lanka with a passion for creating dynamic and engaging digital experiences. </p> </div> ) } Desktop 8-6-2024 1-34-01 Am (2) (2) (2) (2).mp4- 2 replies
-
- scrolltrigger
- scroll
-
(and 2 more)
Tagged with:
-
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!
- 3 replies
-
- scrolltrigger
- react
-
(and 1 more)
Tagged with:
-
Hello, I'm struggling with making my timeline with ScrollTrigger work on resize. It works perfectly every time if I refresh the page, but it doesn't update properly on resize. I have applied both of below: invalidateOnRefresh: true x: () => value(instead of x: value) What am I doing wrong? I am attaching my whole component code (I have just stripped imports). The aim is to simply make it appear as horizontal scroll for the list. I will appreciate any help. Thank you. export function Companies({ data }: { data: Sanity.Company[] }) { const listRef = useRef<HTMLUListElement>(null); const itemsRef = useRef<(HTMLLIElement | null)[]>([]); const tlRef = useRef<gsap.core.Timeline | null>(null); useGSAP( () => { gsap.registerPlugin(ScrollTrigger); const mm = gsap.matchMedia(); mm.add("(min-width: 1024px)", () => { if (!itemsRef.current[0]) { return; } tlRef.current = gsap.timeline({ scrollTrigger: { trigger: listRef.current, start: "bottom bottom", end: `+=3000`, scrub: 2, pin: true, invalidateOnRefresh: true, }, }); tlRef.current.to("li:nth-child(-n + 5)", { y: "0%", stagger: 0.1, duration: 1, }); tlRef.current.to(listRef.current, { invalidateOnRefresh: true, x: () => `-${itemsRef.current[0]?.offsetWidth * itemsRef.current.length - window.innerWidth}`, duration: 4, }); }); }, { scope: listRef } ); return ( <ul className="grid grid-cols-2 gap-x-8 gap-y-24 px-15 pb-52 sm:grid-cols-3 lg:flex lg:w-fit lg:flex-nowrap lg:justify-between lg:gap-0 lg:px-32" ref={listRef} > {data?.map((company, index) => ( <li className={cx(["lg:pr-[7vw]", index < 5 ? "lg:translate-y-full" : ""])} key={index} ref={(el) => { itemsRef.current[index] = el; }} > <Company {...company} /> </li> ))} </ul> ); }
- 2 replies
-
- scrolltrigger
- nextjs
-
(and 2 more)
Tagged with:
-
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;
-
Hello! I'm trying to create a carousel similar to this website: https://wtcabuja.com/ As you scroll in reference site, a single item stays in the middle, scrolling fills the progress below and when it completes, the next slide comes from the right side and vice versa for reverse. I tried implementing something similar by sliding an item using ScrollTrigger. But I don't know how can I manage to make multiple items slide back and forth on scroll. Here's a Stackblitz Minimal Demo of what I did. Any kind of help is appreciated. Thanks!