Jump to content
Search Community

GSAP & Vue & VueRouter: transition hook does not fadeOut

Robert Wildling test
Moderator Tag

Warning: Please note

This thread was started before GSAP 3 was released. Some information, especially the syntax, may be out of date for GSAP 3. Please see the GSAP 3 migration guide and release notes for more information about how to update the code to GSAP 3's syntax. 

Recommended Posts

This problem has some previous posts that started here: 

But since my problem turned out to be something completely different the initial title suggested, I decided to start a new thread. Hope that's ok! :-)

The Pen shows a little Vue app, where GSAP is used in the transition hooks. A GSAP animation outside of the transition component fade/slide in the nav. The initial display of the intro fading in, then fading out and eventually showing the main component and the navigation component works so far.

 

Now, when I want to click on "Back to Intro", which is defined as a router-link (which is different compared to the intro button), the intention would be:

1. fade out the main content

2. fade in the intro again.

 

Should be simple, one might think.

But for some reason the fadeout on the main component does not work. At least it is not visible, because the feedback in the console does show that there is something going on...

 

 I was also experimenting with using the leave hook, but to no avail.

 

What am i doing wrong?

 

BTW: I really tried hard to keep the code to a minimum, but for one: I need the comments, and for the other: it seems, a certain amount of code if just necessary... I hope you still are willing to have a look at it! Thank you!

See the Pen BOLQOJ by rowild (@rowild) on CodePen

Link to comment
Share on other sites

Sorry, I don't know enough about Vue to be able to untangle all that. 

I have to imagine getting to the core of the problem can be illustrated in much less code. I know you tried, but 100+ lines of code to essentially toggle between 2 states seems like an awful lot. 

 

Regardless, I added another layer of debugging as it may help you or someone else figure it out.

 

You will notice in the demo below 3 boxes for tlMain, tlNav, and tlIntro. Each one has a tween in its respective timeline that moves it down 50px.

 

if a box is moving down it means its timeline is playing forward. if it is moving up it means it is reversing.

 

from what I can tell the timelines are playing and reversing in the order you are expecting:

 

See the Pen zJKgyj?editors=0010 by GreenSock (@GreenSock) on CodePen

 

To me it seems like the references to the targets of the various containers you are using are being lost somewhere as they appear to be stuck in their state of autoAlpha:0  which applies the inline styles of visibility:hidden and opacity:0

 

 

 

  • Like 3
Link to comment
Share on other sites

I'll have to agree with Carl here, it really seems to be excessive and a bit convoluted at the moment.

 

Even though I have been following this adventure of yours for a little while now, I am still struggling to visualise what is the journey you are trying to implement.

 

What you are doing wrong, in my view, is trying to have one parent component controlling all its children behaviour. What you really need to do is to have the children take responsability for their own behaviour and only report to the parent whenever they acomplish something.

 

Vue already has all the behaviour you are trying to achieve baked into the framework, you're just not really tapping into it. Currently your components are nothing but HTML templates when they should be self-contained components with their own data and logic.

 

You should stop what you are trying to do at the moment, go back to the Vue docs and digest the different patterns already designed by the framework. Because, what you are describing here is basic routing but you are not following Vue's pattern for routing and transitioning between routes.

 

Here's a fork of your pen after dieting.

 

See the Pen xaRKRB by dipscom (@dipscom) on CodePen

 

  • Like 6
Link to comment
Share on other sites

Thank you again for your feedback!

 

@Dipscom there are times you use Timelines and others where you use TweenMax. I need timelines, though, because it eventually is not just a simple fadein/fadeOut problem, but a problem of how to populate the Timeline with all the needed DOM elements, that are not all available at the same time in the transition component that needs to be synced to components outside that transition component.

 

I will think about your version - thank you! -, but it strips away the problem...

 

@Carl Thank you for your pen, too! It demonstrates one thing, that I thought should not be done, that is: crawling the DOM each time a tween is called by passing in a direct CSS class. I know that it is not a problem for a few little tweens, but this is a stripped-down version of my problem, and one essential thing about the problem is how to get the DOM elements. As mentioned above, the live cycle of Vue and its transition hooks do not guarantee the access to all DOM elements immediately. That reason - and the one of not crawling the DOM with each call - are the reason, why I actually thought a settings object would be a way-to-go...

 

So just to clarify: If it was just about fadeIn/fadeout in the transition component itself, I firstly wouldn't need GSAP and secondly would have my app working so far. But is is about GSAP, its callback system, and Vue's transition component and its callback features, which need to communicate and get synched with GSAP-animated elements outside itself. (Because at a later point there is axios...)

But for the moment, I will leave you in peace! You've been incredibly supportive - thank you very much again!!!

Link to comment
Share on other sites

Sorry, may I ask one more thing, please (this seems to be very GSAP related now...):

I took @Dipscom's example and added animation to the nav component again, copying the way you do it with show() and hide() methods that call a TweenMax(). The (working) pen is here:

 

See the Pen MqbEQX by rowild (@rowild) on CodePen


But the thing is that I need timelines, that are ready to be called. So I tried this:
 

tween_nav() {
    TweenMax.to( '.navi-wrapper', 1, {
        autoAlpha: 1,
        y: 0,
        onComplete: function() {
          console.log("tween_nav finished")
        }
      })
    },
    tl_toggle() {
      console.log("Initialize tl_toggle... (called from created())")
      this.tlNav = new TimelineMax({
        paused: true,
        onComplete: this.done
      })
      this.tlNav.add( this.tween_nav, 0 )
    },
    done() {
      console.log('finished nav fading/sliding).')
    }
  }

 

At this point I cannot call tl_toggle() directly, because the function that creates the timeline has not been called, right?
So in a next step the function gets called in created(). In order to not start the animation directly, a "paused: true" is added ti the timeline definition.
 

created() {
  // Accessing this.tlNav directly does not work until the function creating the tl hasn't been called.
  // In order not to execute the tl immediately, the timeline attribute "paused: true" is required.
  this.tl_toggle()
},

 

Now, I can call this.tlNav() directly from the Nav component's "watch" attribute:
 

watch: {
  $route(value) {
    console.log('watching', value.path)

    if(value.path !== '/') {
      this.tlNav.play()
    } else {
      this.tlNav.reverse(0)
    }
  }
}, 

 

...but the timeline is always playing forwards. I would love to be able to have the reverse functionality available here, but - again (still?) - I do not understand how to setup such a timeline in Vue... I wonder if this is showcasing the troubles I have a bit better?

 

This would be the (not working) pen:

 

 

See the Pen yxVvez by rowild (@rowild) on CodePen

 

 

Link to comment
Share on other sites

Got it: I forgot a the "return" in the TweenMax, the "()", when calling the function, and the initialisation of the timeline needs to be done in mounted() (not created(), because in the latter the DOM is not ready yet. "mounted()" fires after the transition hooks and therefore is at some point a problem...

 

The last pen is updated.

 

OK, done here. Thanks again for your patience and help!

My next post: the working result :-)

 

Happy tweening!

  • Like 1
Link to comment
Share on other sites

@Dipscom A theoretical question: the `done` callbacks from `appear` and `leave` actually have different context, right?

 

If so, setting a timeline's `onReverseComplete` to `done()` during the `appear` transition (using the `appear`'s `done` callback) would not make much sense, since the timeline's reverse() is triggered from the `leave` transition hook, whichs `done` callback has its own context ... is that correct or BS?

Link to comment
Share on other sites

The done callback does something completely different in each method.

 

As an example, here's a demo where I was showing somebody how a leave method might work using vanilla JS. The callback is a promise that removes the element. An enter or appear method would do something completely different.

 

See the Pen d16160067f1290311f6eca23864f0fde by osublake (@osublake) on CodePen

 

  • Like 2
Link to comment
Share on other sites

2 hours ago, Robert Wildling said:

A theoretical question: the `done` callbacks from `appear` and `leave` actually have different context, right?

 

Strictly speaking, no. The context is exatcly the same. It is the component itself.

 

This what you describing will never work because Vue will stop everything it is doing and wait for the done() from the appear method. No further methods will run, no other behaviour will be trigered, nothing. And, given that the done() is never called, the leave hook will never be fired. So, you cannot have execute this idea of yours.

 

You will need to have one animation for the appear method and one for the leave method.

  • Like 3
Link to comment
Share on other sites

@Dipscom Well, the intention was to use the "normal" timeline for the appear hook, and the reversed for the leave hook. Since each has its own callback (onComplete, onReverseComplete), the "done"s could be fired accordingly.

 

But the setup of the timeline has to be initialized at some point (see "appear"), otherwise the reverse version in "leave" is not triggerable.

 

methods: {
  buildUI(el, done) {
    this.tl_intro = new TimelineMax({ 
      paused: true,  // needs to be paused
      onComplete: done,
      onReverseComplete: done // this one won't work
    })
    
    [...]
    
    // do not play the timeline yet!
     return tl_intro
  }
},
appear(el, done) {
  // here the buildUI is initialized, so that this.tl_intro is available later
  // cannot do this in beforeAppear, because there is no "done" callback "available"
  this.buildUI(el, done)
  
  // Now play the timeline
  this.tl_intro.play()
},
leave(el, done) {
  // Call the reverse timeline, done won't be triggered, though
  // "el", but especially this "done" cannot be passed to another callback
  this.tl_intro.reverse(0) 
}

 

What happens in appear, looks mega-ugly!!! But, just to make clear: this is only a theoretical and only meant for discussion (from my side at least)!

Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now
  • Recently Browsing   0 members

    • No registered users viewing this page.
×
×
  • Create New...