Jump to content
Search Community

gabriel.ortiz

Members
  • Posts

    23
  • Joined

  • Last visited

Everything posted by gabriel.ortiz

  1. Thank you @Rodrigo. The hook itself looks good. I think the issue could be a mismatch of React versions. Looking at the package.json. I'm using version ^18. and the hook is using ^16. I do think there are some breaking changes between these versions. I've ran into issues like this before. The error message: `Uncaught TypeError: Cannot read properties of null (reading 'useRef')` I've read can be associated with version mismatches.
  2. 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
  3. Thanks so much @GreenSock! I appreciate your response! So I got something working for me in case others might encounter the same situation with context. I ended up modifying the react Gsap context hook to cast the return context types with an optional object that you can pass in through a generic /** * Wrapper hook initializes GSAP context object * * @param {RefObject} * @param {gsap.ContextFunc} * @returns {gsap.Context} */ export function useGsapContext< T extends HTMLElement = HTMLDivElement, R extends Record<string, unknown> = Record<string, unknown>, // It's ok to allow an empty function since gsap.context() needs a function argument to initialize // eslint-disable-next-line @typescript-eslint/no-empty-function >(scope?: RefObject<T>, context: gsap.ContextFunc = () => {}) { return useMemo( () => gsap.context(context, scope), // It's not necessary to list `context` as a dependency // eslint-disable-next-line react-hooks/exhaustive-deps [scope], /** * Normally its hacky to type assert - but since this context logic is * isolated in this hook there is little risk for these types causing issues * with other implementations of GSAP context */ ) as Partial<R> & gsap.Context; } /** * These types will be added to the return of the gsap context so typescript * will know to expect these properties if referenced later */ type ContextMembers = { newTab: (newTabKey: string, oldTabKey: string | null) => void; }; const wrapperRef = useRef<HTMLDivElement>(null); const ctx = useGsapContext<HTMLDivElement, ContextMembers>(wrapperRef); useEffect(()=>{ ctx.add("newTab", (newTabKey: string, oldTabKey: string | null) => {....}) }, []); useEffect(()=>{ ctx.newTab("foo", "bar"); },[tabState]); Additionally, I noticed gsap.context() will return undefined if no function argument is passed in. Is this expected? The Context types have the callback function as optional argument, and but it appears to be required type argument or else nothing is initialized. // gsap-core.d.ts function context(func?: ContextFunc, scope?: Element | string | object): Context; Correct me if i'm wrong - but it could possibly be defined like this: function context(func: ContextFunc, scope?: Element | string | object): Context;
  4. I'm no Typescript rockstar either... I know enough to get myself in trouble though haha! Basically I think it would be beneficial if we could pass in an optional object type declarations to the gsap.context() method, telling the context typescript method that it can expect a custom property. Something like this.. (and this is very rough and unpolished) type GSAPHandlers = { newTab: (activeTab:string, oldTab:string)=>void; otherMethods: (stuff:boolean)=>void; ... }; // Pass in expected types // The generic passed in would likely need to be converted to a Partial<>, or else Typescript would complain if the additional method is not present when the function executes. const ctx = gsap.context<GSAPHandlers>(() => { }); // Add the method to the context ctx.add("newTab", (activeTab:string, oldTab:string)=>{...}); // Use the method!! ctx.newTab('foo', 'bar'); @GreenSock are there any other ways to add methods to GSAP context? Context is incredibly helpful because it also allows for concise organization of greensock timelines. I know there are other ways of organizing timelines into methods like this - but the x-factor with context is the access to the revert() method which is brilliant!
  5. Hi there! I'm really in love with the new GSAP Context! It's really cool with working with React! When i add timelines with `context.add()` I get a typescript error because the method does not exist as a property of the context object. So the question is: How do I declare a type in typescript (and GSAP) that I intend to add a specific timeline method to the GSAP context? Here's an example: // Borrowing this context hook from the docs @see: https://greensock.com/react-advanced#useGsapContext export function useGsapContext<T extends HTMLElement = HTMLDivElement>( scope: RefObject<T>, // eslint-disable-next-line @typescript-eslint/no-empty-function context: gsap.ContextFunc = () => {}, ) { // eslint-disable-next-line react-hooks/exhaustive-deps const ctx = useMemo(() => gsap.context(context, scope), [scope]); return ctx; } I see that the return types for context are: interface Context { [key: string]: any; selector?: Function; isReverted: boolean; conditions?: Conditions; queries?: object; add(methodName: string, func: Function, scope?: Element | string | object): Function; add(func: Function, scope?: Element | string | object): void; ignore(func: Function): void; kill(revert?: boolean): void; revert(config?: object): void; clear(): void; } Now I'm adding a method to the context like so: // Init the gsap context const ctx = useGsapContext(wrapperRef); // Adds the timeline method to the context. This useEffect runs only once after initial render useEffectOnce(() => { ctx.add("newTab", (newIdentifier: string, oldIdentifier: string) => { const { current: wrapperEl } = wrapperRef; if (!wrapperEl) return; const tl = gsap.timeline(); if (oldIdentifier) { tl.to(`[data-tab="${oldIdentifier}"]`, { duration: 0.5, scale: 0.9, autoAlpha: 0, }); } tl.fromTo( `[data-tab="${newIdentifier}"]`, { scale: 1.2, autoAlpha: 0, }, { scale: 1, autoAlpha: 1, }, ); }); }); // on state update - uses the timeline we added to the context useUpdateEffect(() => { // Using the method added to context (this works!) But typescript complains this method doesn't exist if ("newTab" in ctx && typeof ctx["newTab"] === "function") { ctx.newTab(activeValue.active, activeValue.prev); } }, [activeValue]); So the timeline works as expected which is great... but I get the following error which is expected because typescript doesn't know there is a "newTab" property on the context object
  6. Thank you this is brilliant @Carl and @GreenSock ! I never thought about tweening the z-index. I really like the approach of staggering two separate tweens. I think I have a good understanding of how it works. As a general principle, when would you consider building an animation using a loop over trying to use staggering? The challenge i initially had with staggering if figuring out the sequencing was tricky to conceptualize.
  7. Thanks @Carl this is a stroke of genius! Sorry for the confusion in my description. Basically what i'm looking to do is very close to this example (in fact i already have a use-case for it). The only difference in my code example is I'm trying to overlap the incoming image on top of the outgoing image. (Gosh i can see how what i was describing was super weird and confusing) PS I really appreciate you taking the time to lend a hand...
  8. As an exercise I've been attempting to modify this tween that creates an infinite loop of images fading in with the first image showing on initial render and then will animate out after a delay. The change I'm trying to make is I want to translate AND fade images in while overlapping the previous image. The sequence I'm looking for is: After initial delay on first image Move old image up AND out right after starting to move the new image up and in on top of the previous image Rinse and repeat I'm having trouble with the timing of the stagger. I'm wondering if a label between the first and the last tween segment would help. I'm also struggling with overlapping image 1 over image 3 when the loop starts all over again. I'm sure adding a z-index somewhere would help but I don't know where. I think I could do a lot of cool stuff if I can master this pattern. In many ways this expected behavior is very similar to @Carl 's stagger example in the "GSAP: Beyond the Basic course" Greensock Staggers with Seamless Loops but The only difference I'm trying to merge the logic to have the first item visible on load and starts animating after a delay. Any help would be so so appreciated
  9. haha i'm talking to my manager about that RIGHT NOW! At a minimum can I buy yall a cup of coffee or a beer somehow?
  10. @GreenSock I appreciate so much you making an exception to explain these rationales. This is really invaluable because I can leverage these strategies in the future. I of course will attribute this forum and the original horizontalLoop in my work. I really do appreciate your time.. thanks!
  11. HI! I always have to start off by saying how much I love Greensock. It changed my life (not joking). So i've been working to adapt a typescript version of the `horizontalLoop` method as described in the https://greensock.com/docs/v3/HelperFunctions#loop helper functions. I plan to use it in React. I have a working version of this in a codesandbox: https://codesandbox.io/s/gsap-marquee-loop-494fwh?file=/src/App.tsx Here is the marqueeLoop class: https://codesandbox.io/s/gsap-marquee-loop-494fwh?file=/src/lib/maequeeLoop.ts If i may make a humble ask.. I was hoping someone could explain to me the rationale for the logic behind the refresh() method on the window window.resize event. I would really appreciate the opportunity to learn. Here's the code: /** * Recalculates the the widths of the timeline, calculates the offsets * * @param deep {boolean} */ refresh(deep?: boolean) { // Take a snapshot of the tl progress const progress = this.tl.progress(); // Reset the tl to the beginning this.tl.progress(0, true); // repopulate the widths of the elements this.populateWidths(); // rebuilds the timelines deep && this.populateTimeline(); // populate the offsets between the timelines this.populateOffsets(); deep && this.draggable?.[0] ? this.tl.time(this.times[this.curIndex], true) : this.tl.progress(progress, true); } So my questions are: Why do we take a snapshot of here const progress = this.tl.progress(); Why do we reset the tl progress to 0? this.tl.progress(0, true); Is this intended to pause the timeline while we recalculate? What is the rationale for either using the tl.time or thetl.progress based on Draggable? I don't understand the difference between these. Likewise, in the `Draggable proxy`, InDraggable.onPressInit, we are killing the tweens gsap,killTweensOf(self.tl). Why are we killing the tween? Why are we also setting the proxy values here? gsap.set(self.proxy, {x:startProgress / -ratio} ) ? Also why does `Draggable` return an array? this.draggable = Draggable.create(this.proxy, { trigger: this.container, type: "x", onPressInit: () => { // Kills all the tweens actively in play gsap.killTweensOf(self.tl); // Take a snapshot of the tween progress startProgress = self.tl.progress(); // Recalculate the tl spacing and width self.refresh(); // Offset how much drag happens ratio = 1 / self.totalWidth; gsap.set(self.proxy, { x: startProgress / -ratio }); }, onDrag: function () { self.tl.progress( progressWrap(startProgress + (this.startX - this.x) * ratio), ); }, onThrowUpdate: function () { self.tl.progress( progressWrap(startProgress + (this.startX - this.x) * ratio), ); }, inertia: false, onRelease: syncIndex, onThrowComplete: syncIndex, }); I hope it's not too much to ask - but i would love to understand how this all works. Thanks so much!
  12. GAFF! you are totally right, that was the the ticket. Thank you for pointing it out I was basing my what i know from the "Animating Routes" example If you could spare a second @Rodrigo i was wondering if you could share insight into why we use a ternary to set the styles for the animations, ie: Does addEndListener run on both mount and unmount? <Transition unmountOnExit in={props.show} timeout={1000} onEnter={node => TweenLite.set(node, startState)} addEndListener={ (node, done) => { TweenLite.to(node, 0.5, { autoAlpha: props.show ? 1 : 0, y: props.show ? 0 : 50, onComplete: done }); }} > So does it work like this: onEnter => set styles before mount addEndListener (props.show is true) => entering mounting animation addEndListener (props.show is false) => existing unmounting animation Thanks for the insight!
  13. Hi @ZachSaucier, Thanks for pointing out the legacy GSAP, I switched out the basics right away. And apologies about the confusing layout, it's not very good it's just for prototyping. It's not responsive at all. But on the left and right on the container there are arrows, and that triggered the image to animate. What I'm noticing is the image transition will properly play when the new image is mounted, but it won't play the transition when the image is unmounted. I basically thought that I could control the entering image using the `onEntering` property, and use `addEndListener` to control the image existing.
  14. I'm noodling around with React/TransitionGroup/GSAP and I'm attempting to make a carousel. From an array of images, I'm setting the state to be one object. Using transition group, I want to leverage greensock to transition one image out, followed by one image in. I'm using state to keep track of the "active" image, there is only one image in the state. So the rationale is, when this state update, the old state will transition out, and then replaced by the new node in the state. So there isn't a group of components per-se. I'm hoping that I can get Transition group to transition the old image out, while transitioning the new image in.. I've gotten some stuff to work thanks to posts from @Rodrigo. But I'm definitely stuck now. Here is the codesandbox: https://codesandbox.io/s/carousel-gsap-transitiongroup-z9pd3?file=/src/ImgCard.js I feel like the issue has something to do with this: <Transition timeout={500} mountOnEnter unmountOnExit appear in={show} onEnter={(node) => TweenLite.set(node, startState)} addEndListener={(node, done) => { TweenLite.to(node, 1, { autoAlpha: show ? 1 : 0, y: show ? 0 : 50, onComplete: done }); }} > <li> <img src={cardData.image} alt={cardData.name} /> </li> </Transition> Any advice would be throughly appreciated!
  15. @Rodrigo You were 100% right! Yaahooooooo!!! I never would have figured that out. Storing the tween in `useRef` was the key. I didn't realize that all variables would be overwritten when the component re-renders. I suppose that in the future if we need to save variables in between renders then we should store it in `state` or `useRef` Thank you so much for your help. I believe this example with give me a solid structure for using React and GSAP. Thanks again!
  16. Hi @Rodrigo Thank you for responding! here's a link of Codesandbox.. i hope it's editable: https://codesandbox.io/live/LEpRg I am using `useRef` to grab the element, but I might not be using it the right away. I've edited the code above to show the complete component. Sorry for the confusion.
  17. I've been trying to master using React and Greensock. But I'm having major issues with actually getting the animation to run. To be fair I'm new to React. To practice, I've been trying to recreate @Rodrigo's modal toggle example ( Rodrio's State Controlled Modal ) with React Hooks. I'm successful in passing all the props to the stateless component - but no matter what I try, changing the boolean value in the `props.visible` does not actually play the timeline. I'm not sure if my issue is React, or Greensock. I've logged a series of messages to the console, and I can see that all my functions are working correctly, I can also see that my `useEffect` is firing when `props.visible` changes. https://codesandbox.io/s/quirky-bush-gjn3v?file=/src/modal-component.js import React, { useRef, useEffect } from "react"; import { TimelineLite, CSSPlugin } from "gsap/all"; const ModalComponent = props => { const modalTween = new TimelineLite({ paused: true }); let modalWrap = useRef(null); let modalDialog = useRef(null); console.log("props", props); //useEffect for creating the animation and assigning it to the refs useEffect(() => { modalTween .to(modalWrap, 0.01, { autoAlpha: 1 }) .to(modalDialog, 0.25, { y: 50, autoAlpha: 1 }, 0) .reverse(); }, []); //on props.visible change, we are playing the animation //this only fires when props.visivle changes useEffect(() => { console.log("inside useEffect", props.visible); modalTween.reversed(!props.visible); }, [props.visible]); return ( <> <div className="modal" ref={div => (modalWrap = div)} onClick={props.handleClose} > <div className="modal-dialog" ref={div => (modalDialog = div)}> <div className="modal-content"> <div className="modal-header"> <h4>A Simple Modal Tween</h4> </div> <div className="modal-body"> <p> Lorem ipsum, dolor sit amet consectetur adipisicing elit. Totam velit provident sunt iusto ratione dolore veritatis deserunt ullam vel doloribus. </p> </div> <div className="modal-footer"> <button className="btn btn-secondary" onClick={props.handleClose}> Close </button> </div> </div> </div> </div> </> ); }; export default ModalComponent; Does anyone had any advice on how I can get this thing working? It's driving me crazy! Is there a better approach to this all together?
  18. @Sahil hey i was wondering, since this animation is based on scroll, do you have any performance optimization recommendations? For starters, I put a throttle on the window scroll event of 150ms. So the scroll function has a slight delay to reduce throttling Sometimes i notice a lag and a delay in animation when i'm interacting with the background. I suppose i could reduce the FPS... do you have any other thoughts? Thanks so much, i really appreciate your expertise! -Gabriel
  19. @ZachSaucier yea i see what you mean. I'll continue to mess around with it. Thank you for your help and the inspiration. They key as you suggested is adjusting the viewBox. Thanks!
  20. @ZachSaucier Thank you so much for your help! You are 100% correct, the issue is that the beginning of the SVG needs to overlap with the end in such a way that makes it seem seamless. It's really difficult to do, and what i found from the modifiers demo was that the re-positioning of the box happened outside the overflow of the parent container --- so that's why it has a seamless effect. Can you suggest any way to achieve an infinite seamless effect? Thank you again for your help!
  21. I'm working on a project to infinite scroll a set of diagonal lines in an SVG. I had some massive help from @Sahil (thank you ) who added easing effect on mouse scroll. He also suggested that I incorporate an infinite seamless loop of these shapes using the `modifers` plugin. So i pulled what I could from https://cdpn.io/QEdpLe I'm basically have 2 issues: There's a noticeable jump between the end of one loop and the beginning of the next (See attached video: https://drive.google.com/uc?id=1GfIHd98vs85SOCRdOa45OmzsvjCFanJG). It's not quite right `repeat : -1` doesn't work. I think it has to do with the proxyTimeline. When i try infinite repeat, the diagonals fly through the viewBox and it does not look good. Here's what I've figured out: Based on the "ModifiersPlugin:Seamless Loop" demo, We need to force each child item to advance simultaneously toward the distance of it's parent container, in this case it would be the `viewBox` To position the next item back at the beginning, we use the `modifiers` callback and modulus to fix the next diagonal to the bottom of the viewport, using the height value of the `viewBox`. The "ModifiersPlugin:Seamless Loop" demo also using an overflow to hide each element moving the left position. Somehow I might need to move the transition diagonal to happen outside of the `viewBox` but i'm not sure how to do that Does anyone have suggestions on how I can make the loop more seamless?
  22. @Sahil Terrific!! thank you so much! i understand what i was missing. From here I'll work on making it infinite. I really appreciate this!
  23. Hey yall, I'm new to greensock, and I love the library and this forum. This forum has especially been helpful as I practice and learn gsap technique. I have a new project that is supposed to animate a background SVG, and it has the following requirements: The background diagonals are supposed to slowly move downwards, kind of like a parallax effect. It's a big SVG with lines running across it When the user scrolls, it supposed to speed up the parallax progress a little bit with some easing, but continues to progress down the timeline When the user scrolls upwards, its supposed to reverse the tween timeline I've been able to achieve a few things, but the things i'm still having trouble with are Reversing the scroll direction with the scroll event is upwards. Also right now, I have the progress of the scroll animation tied to the height percent of the `window.innerHeight`. but after testing, i don't think this is a good idea. Because the window height percent is not compatible with the progress of the timeline. So i should find another way to progress the timeline.. but i'm not sure what. For reference I used this post to get started, which as been really helpful: If anyone has advice from doing something similar to this, please please let me know. My knowledge is not advanced enough to debug. Thank you Thank you!! PS please excuse the messy code, i'm just trying to get this thing working. Best, Gabriel
×
×
  • Create New...