Search the Community
Showing results for tags 'react'.
-
I'm just starting out with React and `useGSAP` and I'm running into an issue with out what appears to be the hook registering: react.development.js:1630 Uncaught TypeError: Cannot read properties of null (reading 'useRef') at useRef (react.development.js:1630:1) at useGSAP (index.js:32:1) However, despite this error - I can console.log the `useGSAP` hook, and also the `gsap` package and both are successfully loaded. This error message happens when I try to use useGSAP. This is the error message i'm getting: Segment.tsx:39 Warning: Invalid hook call. Hooks can only be called inside of the body of a function component. This could happen for one of the following reasons: 1. You might have mismatching versions of React and the renderer (such as React DOM) 2. You might be breaking the Rules of Hooks 3. You might have more than one copy of React in the same app See https://reactjs.org/link/invalid-hook-call for tips about how to debug and fix this problem. printWarning @ react.development.js:209 error @ react.development.js:183 resolveDispatcher @ react.development.js:1592 useRef @ react.development.js:1629 useGSAP @ index.js:32 (anonymous) @ Segment.tsx:39 react.development.js:1630 Uncaught TypeError: Cannot read properties of null (reading 'useRef') at useRef (react.development.js:1630:1) at useGSAP (index.js:32:1) at Segment.tsx:39:1 at renderWithHooks (react-dom.development.js:16305:1) at updateForwardRef (react-dom.development.js:19226:1) at beginWork (react-dom.development.js:21636:1) at HTMLUnknownElement.callCallback (react-dom.development.js:4164:1) at Object.invokeGuardedCallbackDev (react-dom.development.js:4213:1) at invokeGuardedCallback (react-dom.development.js:4277:1) at beginWork$1 (react-dom.development.js:27451:1) here's what i have so far: import React, { forwardRef, useEffect, useCallback, useRef } from "react"; import { useActiveSegment, BkExpeditionContextValues, } from "src/BkExpedition/index"; import { mergeRefs } from "src/util/mergRefs"; import clsx from "clsx"; import gsap from "gsap/dist/gsap"; import { useGSAP } from "@gsap/react"; gsap.registerPlugin(useGSAP); export type SegmentProps = Omit< React.ComponentPropsWithoutRef<"section">, "onAnimationStart" | "onDragStart" | "onDragEnd" | "onDrag" | "onDragOver" > & { segmentKey: BkExpeditionContextValues["activeSegment"]; }; export const Segment = forwardRef<HTMLDivElement, SegmentProps>( (props, ref) => { const { segmentKey, className, children, ...restOfHtmlAttrs } = props; const { activeSegment, previousSegment } = useActiveSegment(); const isActive = activeSegment === segmentKey; const sectionRef = useRef<HTMLDivElement>(null); const animateInTL = useRef<gsap.core.Timeline>(); const animateOutTL = useRef<gsap.core.Timeline>(); useGSAP(() => { // console.log("sectionRef", sectionRef.current); // if (sectionRef.current === null) return; // // animateInTL.current = gsap // .timeline({ paused: true }) // .fromTo(sectionRef?.current, { x: "100%" }, { x: "0%" }); // animateOutTL.current = gsap // .timeline({ paused: true }) // .fromTo(sectionRef?.current, { x: "0%" }, { x: "-100%" }); }); useEffect(() => { if (previousSegment === null) { return; } if (activeSegment === segmentKey) { animateInTL?.current?.play(); return; } if (previousSegment === segmentKey) { animateOutTL?.current?.play(); return; } }, [activeSegment, previousSegment, segmentKey]); return ( <section ref={mergeRefs(ref, sectionRef)} {...restOfHtmlAttrs} aria-hidden={!isActive} className={clsx(className, styles.segment, isActive && styles.isActive)} style={{ transform: isActive ? "translateX(0%)" : "translateX(100%)", }} > {children} </section> ); }, ); Segment.displayName = "Segment"; Am i not registering the book in the right place? Should it be registered inside the function component? FWIW - I plan to use this component in Next.js. Any help would be really appreciated -Gabriel
-
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()); }; }, []) -
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
-
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?
-
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!
-
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.
-
Hello, I've started my React journey recently. For my final university college project, I want to create a "Your own adventure webcomic". I want to animate the every panels and in order to do so I had to use GSAP or "useGSAP" (I am proud of this one). I have watched those two videos to understand how it works: https://www.youtube.com/watch?v=DwU72sp_gGI https://youtu.be/l0aI8Ecumy8 But I am not sure if I have fully understood how it works. useGSAP automatically revert the animation at the end, so does it mean that I am not supposed to see things like this once the animation finished? Or is it only when the component is dismounted? I also wanted to ask if I use it properly. So here is an example of how I have used it in my code: const [isMounted, setIsMounted] = useState(false) const shakingRef = useRef(gsap.timeline({ repeat: 2, repeatDelay: 0 })); useEffect(() => { setIsMounted(true); }, []); useGSAP( () => { if (isMounted == true) { if (healthRef.current > elvan.health) { shakingRef.current .to(elvanRef.current, { xPercent: -5, yPercent: -5, duration: 0.1 }) .to(elvanRef.current, { xPercent: -0, yPercent: -0, duration: 0 }) .to(elvanRef.current, { xPercent: 5, yPercent: 5, duration: 0.1 }) .to(elvanRef.current, { xPercent: -0, yPercent: -0, duration: 0 }); } } }, { dependencies: [elvan.health], scope: elvanRef.current, } ); Thank you in advance for your answer (And sorry if I have made some mistakes, English is not my mother tongue).
-
Hello, For my final university college project I want to create a "Your own adventure webcomic". I've recently started my React journey, and I wanted to animate some stuffs in this project. Here you can access a codesandbox for a better understanding for what is coming. I think that I have a misunderstanding of how the scope of useGsap works. Here is my problem, I have a parent component (StoryNarrative) and its child (StoryFragment). StoryNarrative create multiple iteration of StoryFragment. It is important to also note that a new StoryFragment is created whenever the state displayToNumber changes. displayToNumber is a number that changes everytime that the user clicks on a button. It tells StoryNarrative to loop StoryFragment based on an array from 0 to displayToNumber. I want the scope to be a div within the last iteration of StoryFragment that has been created. In order to do so, I have created a ref array (animatedRef) that refers every div. Once done, I have written that the scope should be the last element created. I thought that I could have two different ways to do so. Use animatedRef.current[animatedRef.current.length - 1] or animatedRef.current.[displayToNumber]. But it does not work as I thought. The console shows that for some reason, even after that a new div is created, React keeps using the old length for the first build of the component before changing when I use "animatedRef.current[animatedRef.current.length - 1]" but it does not do so when I use animatedRef.current.[displayToNumber]. But the problem is, however I write animatedRef.current.[displayToNumber] or animatedRef.current[animatedRef.current.length - 1] as the scope, it never refers to the last element created. With animatedRef.current[animatedRef.current.length - 1] it animates the element before the last and with animatedRef.current.[displayToNumber] it seems like the scope does not work at all. So if anyone have any idea of why this is happening please let me know. English is not my mothertongue so I may have done some mistakes, if something is not clear enough please let me know. And thanks in advance for your answers.
-
useGSAP hook cleanup does not appear to be working with scroll trigger
Nightshift posted a topic in GSAP
I'm using the new useGSAP hook with gsap + scroll trigger. When I use the hook, my scrolltrigger pinned section seems to disappear. When I use the original way (using useIsomorphicLayoutEffect with GSAP Context) it seems to work exactly as expected. Im using packages: - GSAP business - useGSAP version 2.1.1. - React version 18.3.1 - React DOM version 18.3.1 Unfortunatley when I create a minimal demo, both methods work fine Is there something I should be looking for, or am i not using the hook correctly? See video here for example => https://vimeo.com/947131601?share=copy // this works useIsomorphicLayoutEffect(() => { const ctx = gsap.context(() => { gsap.to("#voting", { scrollTrigger: { trigger: "#voting", pin: "#voting", start: "top top", end: "bottom top", }, }); }); return () => ctx.revert(); }, []); // this does not work useGSAP(() => { gsap.to("#voting", { scrollTrigger: { trigger: "#voting", pin: "#voting", start: "top top", end: "bottom top", }, }); }, []); -
Hello everyone, I want to switch component to render using gsap but currently it overlaps eachother. my current code is: https://stackblitz.com/edit/stackblitz-starters-egrwn8?file=app%2Fpage.tsx
-
Gsap `.then()` callback func doesn't get triggered when running tests
Hashira posted a topic in GSAP
Hi there, So I am using gsap for animation in a NextJs project and there are some component that has little animations that I am trying to test. To be direct I stimulate a user click interaction in the test and check to see if the "aria-*" attribute or "data-*" attribute has been updated to the correct state when the animation is done but all test fails. I am using the gsap `.then` callback to trigger this update when the animation is done. It works fine in the dev and prod env but the `.then` callback function fails to run in a test env. I have created a minimal demo of my issue, please find the link below https://stackblitz.com/edit/stackblitz-starters-mvcff3?file=app%2Fpage.test.tsx Thank you. -
Hi yall, I'm hoping I can get some help with some gsap animations in React. I have a UI i'm working on where an inactive segment animates by translating up, while a new active element translates to 0 from the bottom.. There is a noticeable flicker of no content before the animation starts. I don't know what I'm doing wrong to cause this. I first noticed it in my storybook. Here's a link to my code transferred to a codesandbox to demonstrate; this is the core of the logic where the animation happens: https://codesandbox.io/p/sandbox/gsap-expedition-w96d45?file=%2Fsrc%2Fcomponents%2FExpedition%2FSegment.tsx%3A189%2C28 You can recreate the animation sequence by clicking on the buttons at the top which should trigger the animations to change. Nothing will happen if you click the same button twice Caveat: I know I should be using useGsap hook - but I can't until we update our react version. So here i use a custom hook instead. Any help would really be appreciated!
- 5 replies
-
- react
- typescript
-
(and 3 more)
Tagged with:
-
scrolltrigger ScrollTrigger Unexpected Behavior with Smooth Scrollbar in React/NextJs
rnrh posted a topic in GSAP
I am having an issue I have seen described in other contexts but haven't found a solution for. My project is using the Next.js framework for react, Smooth Scrollbar, and ScrollTrigger. I have a .to() with a scrollTrigger object inside that is in a <Navigation /> component. The text at the top of the page is just supposed to pin itself and scroll with the window, but it only works in some cases. I think I'm not fully understanding some lifecycle thing here. All these methods of reproducing my errors are also commented at the top of the index.js file in the sandbox With Smooth Scrollbar enabled, the scrollTrigger object stored in the <Navigation /> component, and the <Navigation /> component imported and nested in the main functional component, the effect does not work after refreshing the page. If you go into the scrollTrigger object in the <Navigation /> component and update one of the values (change the 'end' value to a different number) this will trigger a hot reload without refreshing the browser. If you scroll back to the top and scroll down, you will see the effect is now working. If you comment out the entire useEffect() hook in the main index.js component this will disable Smooth Scrollbar. If you save the file and refresh you will see the effect working as intended, without having to trigger a hot reload, but now there is no Smooth Scrollbar. If you reset the sandbox to the original state, uncomment the HTML in the main index.js components return, comment out the <Navigation /> component right above it, save and then refresh, you will see that the effect works on load, with Smooth Scrollbar, without having to trigger a hot reload, but now I've lost the ability to nest components. CodeSandbox link Direct link to results in browser (A little easier for refreshing) Any help would be greatly appreciated. I'm out of ideas at this point.- 20 replies
-
Hi everyone. I'd like to create an animation that plays in a loop and also plays when I scroll. Animation: animates the css clip-path property of the images according to the current index. Currently, when the component is mounted, the animation plays correctly but remains stuck on the last image of the array. And when I scroll, nothing happens. Does anyone have a solution or know where to find the bug ? Thank you in advance Demo link : https://stackblitz.com/edit/gsap-react-basic-f48716-ebgh4d?file=src%2FApp.js I was inspired by the following examples: https://codepen.io/GreenSock/pen/wvZjeGN?editors=1010 https://codepen.io/GreenSock/pen/XWzRraJ?editors=1010
-
code sandbox I'm new to GSAP and am struggling getting a certain animation to work using scrollTrigger when my elements are in a grid layout. The animation aims to move the content horizontally as the user scrolls, and I've taken out the relevant components into this code sandbox. Apologies if there's still too much code in there, but I wanted to make sure the code is representative of the components I have in my project, in case there are nuances causing the problem. In App.tsx, if you uncomment all the code, you'll find a grid layout containing a 'navbar' on the left, and the main content on the right. Currently, with the grid commented out, the animation works fine, but if you include the grid, it all breaks for reasons I don't understand. I've read through the common mistakes for GSAP and scrollTrigger and don't think this is covered. I also have a couple of other smaller issues as well please in the ExperiencePage.tsx component: toggleClass on scrollTrigger doesn't apply the .active class to components. the width variable isn't being updated as the browser window is resized, despite me setting invalidateOnRefresh to true and using a function to define the end attribute in the scrollTrigger configuration. Appreciate any help with this please.
- 12 replies
-
- scrolltrigger
- grid
-
(and 2 more)
Tagged with:
-
Hello, I'm trying to implement mounting and unmounting animations to conditionally rendered elements in my React component, similar to the example given in the Advanced React docs: {!joined && ( <div ref={usernameFormRef}> <Button onClick={() => onSubmit()}> Join </Button> </div> )} {joined && ( <div ref={controlsRef}> <Button onClick={() => onLeave()}> Leave </Button> </div> )} const [joined, setJoined] = useState(false); const { contextSafe } = useGSAP({ scope: controlsScope }); const tl = gsap.timeline(); const onSubmit = contextSafe(() => { tl.to(usernameFormRef.current, { opacity: 0, yPercent: 200, duration: 0.7, ease: "back.in(2)", onComplete: () => setJoined(true), }); tl.from(controlsRef.current, { opacity: 0, yPercent: 200, duration: 0.7, ease: "back.out(2)", }); }); const onLeave = contextSafe(() => { tl.to(controlsRef.current, { opacity: 0, yPercent: 200, duration: 0.7, ease: "back.in(2)", onComplete: () => setJoined(false), }); tl.from(usernameFormRef.current, { opacity: 0, yPercent: 200, duration: 0.7, ease: "back.out(2)", }); }); The problem I faced was that the onComplete function seemed to fire at the end of the timeline, not between each tween. This results in the tl.to unmounting/exit animation working as expected unlike tl.from which results in an abrupt mounting of the controlsRef element. I have also tried implementing without a timeline but ran into the same results. What exactly am I missing here? Any help would be much appreciated. I have also attached my component directly if more context is needed. MultiplayerControls.tsx
- 4 replies
-
- react
- conditional statements
-
(and 2 more)
Tagged with: