Jump to content
Search Community

alvinteh

Premium
  • Posts

    10
  • Joined

  • Last visited

About alvinteh

  1. Hi Rodrigo, thanks for getting back to me! Yes, the issue does not exist in the CodePen demo; I was unfortunately unable to reproduce it in a minimal demo. Question on the FOUC: do you mean that the opacity: 1 style is being set prior to GSAP executing the fromTo()? If so, shouldn't specifying the initial styles via CSS resolve that? On my end, my belief is that GSAP is somehow mistakenly setting that opacity: 1 style as there are no other scripts/CSS influencing that element's display. When I remove the tweens, the inline styles expectedly disappear -> I believe those are being injected by GSAP. Looking at the video I shared, you can see that the two <span>s (i.e. the two children being animated) have opacity values of 1/0.6062, among other styles, that do not tally with the initial values set in the fromTo(). The tween code for the actual project and the CodePen demo are identical. In case it was some form of race condition, I have also tried specifying the initial values in CSS (mentioned in my post), but those are also (expectedly) being overridden by GSAP, though unfortunately with the wrong values (i.e. I expect GSAP to inject opacity: 0, but it is injecting opacity: 1). I also doubt the issue has to do with the tween itself, due to how workaround #2 works. On the flip side, removing the problematic tween only causes the issue to affect the new first tween. My guess is there is some form of race condition somewhere, although how/why that causes GSAP to inject the incorrect initial values is perplexing. Or, if we approach this from another angle, how does GSAP determine and populate the initial style values? Perhaps understanding that will help me identify the root cause.
  2. Yes, I am using the useGSAP() hook. I don't think it's a React issue though, other than the refs, there is nothing React-specific in this case (a contrast from my previous thread). I have tried that (that is how I noticed it the issue, if present, only affects the first tween in the child timeline), but did not manage to progress much further debugging-wise. I also highly doubt the issue has to do with the properties being animated, as the other affected tween(s) involve different properties. The elements also behave/appear correctly when I remove all of the tweens. What I find most peculiar is that this only affects a couple of tweens, and only on the initial playthrough (I have also tested using GSDevTools). Sorry, I don't quite get your question? There is no CSS pertaining to animations and/or transitions, if that is what you are asking, only things like font-family and font-size, which shouldn't impact the animation.
  3. There are issues with a couple of Tweens in a project I'm working on. For some reason, GSAP does not seem to respect the initial/from values for those Tweens on the first playthrough of the timeline. The tween is simple, I'm trying to animate some text elements as follows: timeline.fromTo(headerRef.current.children, { filter: 'blur(4rem)', opacity: 0, scale: 0.05, y: '75px', }, { filter: 'blur(0rem)', opacity: 1, scale: 1, y: 0, ease: 'power1.out', duration: 0.4, stagger: 0.25, }); However, prior to play, the filter/opacity/scale/y values of the respective elements do not reflect the initial/from values indicated in the code above, and as a result, the tween looks incorrect. I have attached a clipped video showcasing the issue. Note how on the opacity and filter blur values of the first <span> are 1/0rem initially. However, when I perform a tweenTo() back to the original position of the timeline, and opacity/blur values are correct, and the subsequent playback is correct. Things I have tried to no avail: Switching the fromTo() to a to() (and not specifying the initial values in CSS) Switching the fromTo() to a to() (and specifying the initial values in CSS) Switching the fromTo() to a from() (and not specifying the final/target values in CSS) Switching the fromTo() to a from() (and specifying the final/target values in CSS) Removing the ease and stagger properties (those alter some of the initial values as expected, but they are still incorrect) Simplifying the properties being animated (e.g. cut everything other than opacity); this expectedly does not resolve the issue Outside of the workaround, no other code is interacting with/manipulating the animated elements. Interestingly enough, this issue only presents itself on the first tween of the timeline (and it does not seem to affect all tweens, only two in my entire project). In addition, while I don't think it should affect things, these are child timelines within a larger parent timeline (from my previous thread). Some workarounds that resolve the issue: (1) The issue is resolved if I add a set() call right after fromTo() to "force" the initial values, i.e: timeline.fromTo(...); // Full code from initial snippet timeline.set(headerRef.current.children, { filter: 'blur(4rem)', opacity: 0, scale: 0.05, y: '75px', }, '<'); (2) Given the issue only presents itself in the first tween, if I add another "meaningless" tween before the actual one, the actual tween works smoothly. timeline.fromTo(headerRef.current.children, { z: '1px' }, { z: 0 }); timeline.fromTo(...); // Full code from initial snippet While I have added a CodePen that describes what I am trying to achieve, I have not been able to reproduce the issue in the CodePen. I appreciate that the lack of a CodePen with the issue makes debugging/assisting this matter more challenging, and the fact that I found workarounds may also make this a lower priority, but if possible, I would still like to understand the cause of the issue and it should be properly resolved. Issue.mp4
  4. I agree that refs should be used in favor of selector text in the context of React apps; that's what I actually did after I saw your previous post + the docs! 😊 Thanks once again GreenSock!
  5. Yes, you're spot on with regards to the new wrapper element that ScrollTrigger creates around the pinned element causing the issue as it's above/outside the DOM nodes React expects to see in the component. When creating my own wrapper <div> (but without specifying the pinSpacer option), the ScrollTrigger-generated wrapper still falls within my <div>, which also solves the problem. Specifying the pinSpacer option helps remove one extraneous div though! EDIT: I think the documentation for pinSpacer option should mention it supports String | Element given it also supports CSS selector text (similar to the trigger option)?
  6. Thanks a lot for the help @GreenSock! As a quick update, I had to refactor a few things (primarily CSS-related) but I have managed to get things working. I don't have a working CodePen at the moment, but in a nutshell, my approach was to: 1. Retain timelines in the <Scene> components; however, I removed ScrollTriggers from them. 2. Adapted the callback section in GSAP Advanced React guide; I used both React context and callbacks for <Scene>s to pass their timelines and "container div" refs to the <Page> components (in a useGSAP call). I had to use context as the scenes were being passed via the children prop. i.e. my code was like: const PageContext = createContext<PageContext>({ registerScene: () => {}, }); const SceneA = ({ sceneIndex }) => { const { registerScene } = useContext(PageContext); useGSAP(() => { const timeline = gsap.timeline({}); timeline.to(...); registerScene(sceneIndex, sceneRef, timeline); }, []); }; 3. In the <Page> component (<PageBase> in the excerpt below), I have a useGSAP call that creates a master timeline with a ScrollTrigger, and stitches together the timelines from each Scene. It also adds animations to transit between each scene. const ParallaxPageWrapper = styled.div` position: relative; height: 100vh; overflow: hidden; `; const PageBase = ({ children }) => { const pageRef = useRef(); const [sceneRefs, setSceneRefs] = useState([]); const [sceneTimelines, setSceneTimelines] = useState([]); gsap.registerPlugin(ScrollTrigger); useGSAP(() => { const sceneCount: number = Children.count(children); if (sceneRefs.length === 0 || sceneRefs.length !== sceneCount || sceneTimelines.length !== sceneCount) { return; } const { totalDuration, totalChildren } = sceneTimelines.reduce((prev, sceneTimeline) => { return { totalDuration: prev.totalDuration + sceneTimeline.totalDuration(), totalChildren: prev.totalChildren + sceneTimeline.getChildren().length, }; }, { totalDuration: 0, totalChildren: 0 }); if (totalChildren === 0) { return; } const timeline = gsap.timeline({ scrollTrigger: { trigger: pageRef.current, pin: true, scrub: true, start: 'top top', end: `+=${(totalDuration + sceneCount - 1) * parallaxUnit}`, } }); for (let i = 0; i < sceneTimelines.length; i++) { const nextSceneRef = sceneRefs[i + 1]; const sceneTimeline = sceneTimelines[i]; timeline.add(sceneTimeline); if (i < sceneTimelines.length - 1) { timeline.add(gsap.to(nextSceneRef.current, { transform: 'translate3d(0, -100vh, 0)', duration: animationDurations.XSLOW, })); } } }, [children, sceneRefs, sceneTimelines); const registerScene = useCallback((index, ref, timeline) => { setSceneRefs((prevSceneRefs) => { const newSceneRefs = prevSceneRefs.slice(); newSceneRefs[index] = ref; return newSceneRefs; }); setSceneTimelines((prevSceneTimelines) => { const newSceneTimelines = prevSceneTimelines.slice(); newSceneTimelines[index] = timeline; return newSceneTimelines; }); }, []); return ( <div> <PageContext.Provider value={{ registerScene }}> <ParallaxPageWrapper ref={pageRef}> {children} </ParallaxPageWrapper> </PageContext.Provider> </div> ); }; 4. From the above, I can now create <Page>s like so: const TestPage = () => { <PageBase> <SceneA sceneIndex={0} /> <SceneB sceneIndex={1} /> </PageBase> ); (Note: I've simplified the code from my original TypeScript code, but the main logic should be there). Some other remarks: I would love to do without sceneIndex but it's the easiest way to ensure scenes don't override each other in <PageBase>. The <div> surrounding <PageContext.Provider> is necessary to avoid React errors. See comment in other GSAP community thread. The useLayoutEffect()/useGSAP() comment later on did not resolve the issue. Hopefully the above helps others trying to achieve similar results.
  7. Thanks for the updated CodePen, it is very helpful! I'm trying it on my end with some adjustments to accommodate multiple animations per <Scene>, and had a few quick GSAP questions. Namely, I noticed in the provided CodePen and article that gsap.to/from/...() is used instead of tl.to/from/...(). (For brevity's sake, I'll just mention the to() methods). 1. What would be the equivalent way of supplying the position parameter (in tl.to()) with gsap.to()? I see the tl.to() is a convenience method for tl.add(gsap.to(...)) but don't see any equivalent of the position parameter in the gsap.to() documentation. 2. I see tl.add() supports different types of child parameters. Just for my understanding, what is the difference between calling tl.add(anotherTimeline) versus tl.add(gsap.to()) multiple times (one for each tween)? For #2, it would also be great to understand the implications, if any, of calling tl.add(anotherTimeline) if the parent timeline has a ScrollTrigger (but not the child timeline, that is only a gsap.timeline({})). Context: I'm trying to understand if it's better/easier for child components to pass a timeline (as opposed to individual tweens) to the parent timeline, particularly if #1 is difficult to achieve.
  8. I do not intend for there to be conflicts between the parent and child ScrollTriggers. I've created another fork of the original CodePen, but with all of the <Scene>s within the <Page>. The animation in this fork is the intended effect and works correctly. https://codepen.io/alvinteh/pen/poYygba?editors=0010 I guess another way of phrasing my question is: given the above, how should I go about splitting the <Scene>s into individual components while still maintaining a single ScrollTrigger-based timeline? PS: Thanks for helping with the useGSAP() hook! I've been using that in my actual code but for some reason, CodePen refused to import it when I was making this post, hence the use of useLayoutEffect().
  9. I have made a quick attempt at my guessed solution and there are other issues, namely how do I pass the timeline to the <Scene> components, and how to time tweens between <Scene>s). CodePen here: https://codepen.io/alvinteh/pen/wvOGByr?editors=0010 (apologies, I haven't been able to figure out how to add a CodePen in a reply rather than a new post). EDIT: scrapped apologies as I see the CodePen link results in an automatic embed.
  10. I have a React project with multiple <Page> components (roughly equivalent to a webpage), that each have one or more <Scene> components (take them as sections within a webpage). These <Scene> components have independent ScrollTrigger-based timelines and animations that work well. I would also like to add ScrollTrigger-based parallax animations to the <Page>components. In the CodePen, this is illustrated on the <Scene>level by the top -= 100vh animation applied to the <SubContent> elements. (i.e. I would like the same animation applied to <SceneB>, as depicted in the commented-out code). What is the best way for me to achieve this? My understanding is that nesting ScrollTriggers is generally a no-no due to conflicting logic between the parent/child timelines. However, I would also like to keep <Scene>-specific timeline information (particularly the duration) within <Scene>s (e.g. <SceneA>should not know about <SceneB>'s timelines). While I'm happy to do some refactoring, I would like to avoid having to make complicated changes to individual timeline.to/from calls where possible (there are already dozens of animations). Combining <Scene>s is also not an option as many of them are large (500+ LOC). Currently, my guess is that I'll need to merge all of the timelines in each <Scene>into one timeline on the <Page>level. However, if I do so, how do I synchronize the start of each <Scene>'s animations to the original trigger and start? In addition, how would I pass the end in each <Scene>to the <Page>-level timeline? Or is there another better way to do this?
×
×
  • Create New...