Jump to content
Search Community

Marco Rodrigues

Members
  • Posts

    17
  • Joined

  • Last visited

Posts posted by Marco Rodrigues

  1. On 2/14/2023 at 4:13 PM, Rodrigo said:

    Hi,

     

    As @mvaneijgen says: "If it works, it works" ;)

     

    What you could explore is add a nextTick callback in your watcher and see how that works.

     

    Although I believe the issue here is that watchers are lazy by design, so you need to get the watcher to work on the first mount and not on every update:

    https://vuejs.org/guide/essentials/watchers.html#eager-watchers

     

    What gets my attention is that while watchers are lazy in Vue while Nuxt uses suspense (which is still in experimental) to get a similar effect, that's why is not included in those examples as it is on the Vue example (line 31):

    https://stackblitz.com/edit/vitejs-vite-w8wtpj?file=src%2Fviews%2FScrollView.vue

     

    Give that a try and let us know how it works.

    Happy Tweening!

    Hello.
    About this issue, I'm having the exact same problem. The stackblitz example works fine, there is just the small problem mentioned above that on a hard refresh the watcher doesn't get triggered and therefore the animation doesn't execute (only on refresh, if you navigate away and come back, th en the watcher will trigger with the animation).
    One way to solve it would be to do something like op said, having a function that will execute onMounted and use that same function inside the watcher. It just doesn't feel right to do and I wish there was a workaround.
     

    I tried using nextTick with the app.vue 'toggleTransitionComplete' but the problem is the same.

  2.   

    12 hours ago, Rodrigo said:

    Hi,

     

    The thing is that the helper function doesn't return the ScrollTrigger instance but the Lottie animation instance, so you have to destroy the lottie animation and kill the ScrollTrigger instance by hand:

    mm.add(
      {
        isDesktop: `(min-width: 1025px)`,
        isNotDesktop: `(max-width: 1024px)`
      },
      (context) => {
        let { isDesktop, isNotDesktop } = context.conditions;
    
        const lotie = LottieScrollTrigger({
          target: ".lottie",
          start: () => "top top",
          path: isDesktop ? LottieDesktop : LottieMobile,
          pin: false,
          markers: true,
          id: "lottie-st-1",
        });
        // Cleanup, destroy the lottie animation and kill ST
        return () => {
          lotie.destroy();
          ScrollTrigger.getById("lottie-st-1").kill();
        }
      }
    );

    Still I'll ping @GreenSock in case He has a better idea of how this can be handled. Since the lottie instance is not a GSAP related element, there is no way the GSAP Match Media can handle that for you.

     

    Happy Tweening!

     

    7 hours ago, GreenSock said:

    Yep, sounds about right. Of course you could tweak the helper function to attach the animation and/or ScrollTrigger instance to the Lottie object so that you can more easily target it, but it's not a big deal to do it the way @Rodrigo suggested. 

     

    Like: 

     

     

     

    Thanks, both answers seem to solve the problem and make total sense!

  3. Hello.
    I'm currently facing a challenge where I have Lottie path for desktop and a different one on mobile.
    The main point is to use lottie with scrolltrigger, so I went with the helper function
    Problem is, if I follow matchmedia syntax on the lottie helper function, when I resize, the lottie doesn't get replaced but instead keeps appending new elements with the path for the current screen width.

    I'm assuming a way to solve this would be to somehow kill/destroy the previous element before adding the new one, but having some trouble doing that.

    Thanks

    See the Pen WNygaxG by marcorodriguesdev (@marcorodriguesdev) on CodePen

  4. 15 hours ago, Cassie said:

    Heya! So you're wanting to combine scrubbed and non-scrubbed animations. This is a common hurdle.

    This thread and answer should get you on the right track

     

    Yes, managed to solve it using the approach shown on that thread.
    Thanks a lot!
    My bad for creating a new thread about this, was using the wrong keywords to search in the forum, should've looked for "combining scrubbed and non-scrubbed animations/scrolltrigger"

  5. Hi.
    Been having some trouble creating an animation sequence without scrub.
    My idea of the animation on the codepen above, is that the on-scroll animation should fade-in/out headlines, zoom-in (into screen) the last arrow, and then sequently in the future show more headlines. Problem is about timing and when one or the other should fade-out for the next one to fade-in.


    This would be the ideal sequece:

    1. First headline fade-out once the 3rd arrow reaches center of screen;
    2. Second headline fade-in right after;
    3. Second headline fade-out when about to reach the last arrow;
    4. Fade-in third headline once the last arrow is already zooming;


    With scrub I managed to do it using position parameter, but I want the animation to be triggered at once without scrub.
     

    See the Pen VwdXdYK by marcorodriguesdev (@marcorodriguesdev) on CodePen

  6. 4 hours ago, mvaneijgen said:

    I was thinking of wrapping the ellipse element in its own container and animated that on scroll. This way you don't have conflicting tweens and they can play at the same time.

     

     

     

    Managed to make it work using this approach, which was better for my use case. Thanks a lot!
     

    1 hour ago, Cassie said:

    Or maybe?
     

     


    You were using the circle itself as the trigger element which is an issue because that trigger is moving,
    Also scrollTriggers are immediately rendered so you'd want to stop that from happening so it doesn't 'precalculate' the animation values.
    Then pop an overwrite 'auto' on the tween itself so it take priority over the initial load tween

     

     

    Thanks for the reply.
    Also works but there is just a small problem. If you run the demo and don't scroll, you will se the ellipse will end up with the value of "scale: 1.6" by inspecting the element. If you scroll after the animation is complete and go back to the top of the page, the value will go back to 1.6, which is the minimum.
    If you run the demo again, this time you scroll down *while* the animation is playing and scroll back up afterwards, you will see that the "scale" value of the ellipse will not be 1.6 but instead will be the value saved when you first scrolled. Don't think I was explicit enough on the thread about this.

     

    • Like 1
  7. 7 hours ago, GreenSock said:

    Yeah, you're creating conflicting tweens. Here's how I'd handle it: 

     

     

    Thanks for the codepen and reply.
    Your solution seems to solve it to some extent, but you can still see a bit of a jump if you scroll too soon on the page.

    The codepen I provided is not accurate compared to the real case I will be using this animation, but the circle will be, after page load, animated into the first section of the page, showing just a bit of it (first tween), and then, if you scroll right after, the circle will be animated into fullscreen making the page full dark background. That's why the circle jump conflict is pretty noticeable at first.
     

  8. Hi.

    I'm trying to animate a circular shape on scroll that sometimes might happen while a previous Tween with the same element is still animating.
    If you take a look at the codepen, once the page is loaded, the circular shape is being animated, but if you scroll while it's still animating, it causes a jump on the circle.

    The espected behaviour is that if you scroll while the Tween is animating, the animation should continue from the previous Scale value. 

     

    I have already tried "overwrite: true" but that doesn't seem to help in this case.

    See the Pen eYMbmgZ by marcorodriguesdev (@marcorodriguesdev) on CodePen

  9. 46 minutes ago, GreenSock said:

    If the start/end values aren't calculated correctly, that means the layout must have shifted AFTER the window's "load" event which would make sense if you're using a framework that's dynamically loading in new routes. So just make sure you call ScrollTrigger.refresh() AFTER you're done shifting around the layout. 

    Even by calling ScrollTrigger.refresh() it seems to not work.
    It might actually be something related to Nuxt 3 onMounted hook that for some reason is being called before the DOM is ready, but I'm not completetly sure.
    Came across this github thread if anyone also seems to have the same issue: https://github.com/nuxt/framework/issues/3587



     

    • Like 1
  10. Hi.

    I have been recently started working on Nuxt 3 projects and was trying to implement GSAP ScrollTrigger on it.
    Everything was working fine until I realized that sometimes, after navigating routes, the ScrollTrigger start/end positions were wrong. 

    I looked on the forum and found similar topics, but nothing came up for Nuxt 3 and I have been stuck with this for a while. 
    I'm a aware this is a problem caused by Nuxt 3 spa hooks, but even after killing a simple scrolltrigger on the unmount hook, it does not work properly once I come back to the same route.

    Here's a simple StackBlitz Nuxt 3 + GSAP demo I created. The animation is pretty simple, the ball in the center is supposed to scale on scroll, which it does, but if you change routes quickly, or after a couple of times, you will see that the markers will be on top of the viewport and not work anymore. ( Seems pretty random since if you keep spamming and switching routes, it will eventually start working again)

    StackBlitz: https://stackblitz.com/edit/nuxt-starter-yjdl5s

    I know Nuxt 3 is pretty recent and is not even released yet, so there isn't much information about it yet, but any potential help or guide on how to solve this is greatly welcomed.

  11. 13 minutes ago, OSUblake said:

    Just do it like you did with the other stuff.

     

    ScrollTrigger.matchMedia({
      "(min-width: 600px)": function() {
        gsap.utils.toArray(".scroll-trigger").forEach(element => {
          if (element.classList.contains("colors-section")) {
            createColor(element);
          } 
          if (element.classList.contains("text-fade-in-out")) {
            createFadeInOut(element);
          }
          if (element.classList.contains("text-animate")) {
            createTextAnimate(element);
          }
        })
        
      },
      "(max-width: 599px)": function() {
        gsap.utils.toArray(".scroll-trigger").forEach(element => {
          if (element.classList.contains("colors-section")) {
            createColor(element);
          } 
        })
      }	
    });

     

    And you may need to use saveStyles.

     

    https://greensock.com/docs/v3/Plugins/ScrollTrigger/static.saveStyles()

     

    It works now. I think my problem was that I was setting the function that had to be run the same on every media query inside an "all" matchMedia.

    Once I added a min-width: 768px breakingpoint inside the matchMedia to execute the function, and a max-width:767 which also executes the same function, it worked.

     

    Thanks for your time and patience.

    • Like 1
  12. 4 hours ago, OSUblake said:

    That was pseudo code because I don't know what you wanted for each media query. The point is to do the creation inside the matchMedia. 

     

    ScrollTrigger.matchMedia({
      "(min-width: 600px)": function() {
        gsap.utils.toArray(".scroll-trigger").forEach(element => {
          if (element.classList.contains("colors-section")) {
            createColor(element);
          } 
          if (element.classList.contains("text-fade-in-out")) {
            createFadeInOut(element);
          }
          if (element.classList.contains("text-animate")) {
            createTextAnimate(element);
          }
        })
        
      },
      "(max-width: 599px)": function() {
        ...
      }	
    });

     

    That's what I have tried before and without success.
    An approach like this makes the scrolltriggers out of order and bug:
    ScrollTrigger.matchMedia({
      "(min-width: 600px)": function() {
        gsap.utils.toArray(".scroll-trigger").forEach(element => {
          if (element.classList.contains("text-fade-in-out")) {
            createFadeInOut(element);
          }
          if (element.classList.contains("text-animate")) {
            createTextAnimate(element);
          }
        })
      },
      "all": function() {
        createColor(".colors-section");
      }
    })

    Even if inside the "all" function I loop again through each ".scroll-trigger" it still doesn't work.
    Updated the pen with the code above.

  13. 12 hours ago, OSUblake said:

    Hi Marco, 

     

    If I understand you correctly, you just need 1 matchMedia. Just call your functions from within that.

     

    ScrollTrigger.matchMedia({
      "(min-width: 600px)": function() {
        createColorAnimation();
        createAnotherAnimation();
        
      },
      "(max-width: 599px)": function() {
        createColorAnimation();
      }	
    }); 

     

    Wouldn't that make the scrolltriggers to be created out of order since there will be multiple mixed on the page? That's why I  was looking to loop through each ".scroll-trigger" element, because that's the class that is common on every single one of them, check if the element contains the class that identifies the scrolltrigger and then call it.

    Kinda what I'm doing here: 

    gsap.utils.toArray(".scroll-trigger").forEach(element => {
      if (element.classList.contains("colors-section")) {
        createColor(element);
      } 
      if (element.classList.contains("text-fade-in-out")) {
        createFadeInOut(element);
      }
      if (element.classList.contains("text-animate")) {
        createTextAnimate(element);
      }
    })



    I don't think I'm supposed to use matchMedia inside the loop, and if I use it outside the loop that will make the scrolltriggers to be created out of order and bug.

     

    Sorry if I'm missing something obvious or understanding incorrectly.

  14. Hello everyone,

    Based on the pen above, how would I disable, for mobile (767px), all the animations but keep only the createColor one?

    What I want for a website I'm making, is disable specific animations/scrollTriggers (mostly the text reveal ones) for mobile, but keep some others for every media query.

     

    Since I'm using functions for each animation and creating them in a for each loop for them to be created in order and not bug, I'm kinda lost how would I achieve responsiveness here. Using ScrollTrigger.matchMedia inside each function might work, but I feel like it's the wrong approach to do it. I have also tried using only 1 ScrollTrigger.matchMedia before the loop, but that would make the scrolltriggers to be created out of order and bug.

    See the Pen ExbqrWr by marcorodriguesdev (@marcorodriguesdev) on CodePen

  15. 15 hours ago, OSUblake said:

    They should be created in the order they appear, and it should work using what I showed above. Notice how I'm passing in the element. The function is supposed to create the trigger for the element being passed in. 

     

    gsap.utils.toArray(".scroll-trigger").forEach(element => {
      if (element.classList.contains("colors")) {
        createColor(element);
      } else {
        createFadeInOut(element);
      }
    })
    
    function createColor(element) {
      ...
    }
    
    function createFadeInOut(element) {
      
      let fadedText = element.querySelectorAll(".faded-text");
      
     		var textFadeInOut = gsap.timeline({
    		scrollTrigger: {
    			trigger: element,
    			start: "top center",
    			end: "bottom top",
    			scrub: 0.1
    		}
    	});
    	
    	textFadeInOut.to(fadedText,{autoAlpha: 1, duration: 1})
    	textFadeInOut.to(fadedText,{autoAlpha: 0, duration: 1}, '+=1')
    }

     

     

    Totally forgot about passing the element in the function. It's now working as it should, thanks a lot!

    Also updated the pen with the solution.

    • Like 2
  16. On 1/31/2022 at 5:58 PM, OSUblake said:

    Hi Marco, 

     

    It's important to create your ScrollTriggers in the order they appear in the DOM. You don't need to repeat your code, just structure things a little differently. For example, you could make a function for each different type of ScrollTrigger, give a common class name to everything that is going to be used for ScrollTrigger, and then sort it out. 

     

    Pseudo code.

    gsap.utils.toArray(".scroll-trigger").forEach(element => {
      if (element.classList.contains("colors")) {
        createColor(element);
      } else {
        createFadeInOut(element);
      }
    })
    
    function createColor(element) {
      ...
    }
    
    function createFadeInOut(elemenet) {
      ...
    }

     

    Hi OSUblake, I updated the pen and created a function for two types of scrolltriggers in the example. Still doing something wrong as it is not giving the expected result.

     

    Also, is this the best approach to create every single scrolltrigger instance by DOM order? Or should I just do this for the pinned ones, which are the ones that get "bugged" if they are out of order. Asking this because I will be having multiple (5/10+) types of scrolltriggers on my website, some for text animations, some for columns reveals, etc, and was wondering if I should create a function for each and use the approach above.

  17. Hello!

     

    Going straight to the point with the problem I'm having.

    I'm creating multiple pinned sections with the "text-fade-in-out" class that you can see on the codepen provided. I also will have somewhere on the website, other pinned colors sections, which is a different scrolltrigger.

     

    Problem is, if I add a fade in and out section below the colors one, the pin breaks in the end and the starting position gets all wrong for the last sections. I think this is caused because of the pin order, and I could use refreshpriority here to help me. The thing is, since I'm using toArray() to create the fade in and out pins, it makes me unable to use refreshpriority (maybe I'm wrong?).

     

    I could just duplicate the code multiple times for each fade in and out section and use refreshpriority, but I think that would be a bad practice, since I could just use the toArray() to help me here and simplify the code.

     

    Is there a workaround for this? 

     

    Sorry if it's a simple answer, but I've looked everywhere and couldn't find much.

    See the Pen gOXPXPo by marcorodriguesdev (@marcorodriguesdev) on CodePen

×
×
  • Create New...