Jump to content
Search Community

Again: Playing a (staggered) timeline reverse

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

Demo: https://codesandbox.io/s/n34pvjxyxj

 

Hi, fellow GSAP'ers!

 

The topic of playing a timeline in reverse keeps troubling me. The Demo above is one, where, within Vue, a parent component calls function on a child component (using $refs). As the console output shows, the functions are indeed called, but the animation is not played in reverse.

 

I do not understand why, because:

- the timeline is returned to a var, right?

- the staggered animations use `staggerFromTo` , even though I assume that using any of the other two should eventually work, too 

 

What am I doing wrong here (again)?

Do I misunderstand the idea of having the timeline saved to a variable (here: animateLogo)?

Are reverse animation on timeline using a stagger feature not possible? (I doubt that! It's GreenSock!)

 

Thanks you in advance!

PS: the actual GSAP-related code parts are kept as short as possible. Please ignore the other stuff, which is just there to wrap the letters... (no, I am still not using SplitText... shame on me!)

See the Pen by (@) on CodePen

Link to comment
Share on other sites

Thanks for the demo. I don't know much about vue so I can't address anything related to how things are set up, but I managed to find the issue.

 

Each time you are calling this.$refs.Logo.animateLogo() you are creating and returning a  new timeline.

Calling reverse() on a newly created timeline only changes the direction of the playhead (reversing it). 

Your newly created timeline has its playhead at a progress of 0 (the beginning), so reversing it doesn't really do anything that you can see.

 

Imagine going back in time a few years and opening up a fresh new VHS tape. If you put the VHS tape in the VCR and pushed the rewind button what would happen? nothing. You would need to play forward in order for it to rewind. Same thing applies to what you are doing.

 

In your code you are doing this

 

methods: {
    play() {
      console.log("Clicked play")
      this.$refs.Logo.animateLogo()
    },
    reverse() {
      console.log("Clicked reverse")
      this.$refs.Logo.animateLogo().reverse() // not good
    }
  }

 

but you can advance the playhead to the end before reversing by doing this

 

 methods: {
    play() {
      console.log("Clicked play")
      this.$refs.Logo.animateLogo()
    },
    reverse() {
      console.log("Clicked reverse")
      this.$refs.Logo.animateLogo().progress(1).reverse() //better
    }
  }

 

demo here: https://codesandbox.io/s/lrzp0p817q

  • Like 2
Link to comment
Share on other sites

36 minutes ago, Robert Wildling said:

- the timeline is returned to a var, right?

 

It's being returned, but you're not saving it.

 

Maybe like this.

 

methods: {
  play() {

    if (!this.logoAnimation) {
      this.logoAnimation = this.$refs.Logo.animateLogo();
    } else {
      this.logoAnimation.play();
    }
  },
  reverse() {
    
    if (this.logoAnimation) {
      this.logoAnimation.reverse();
    }
  }
}

 

https://codesandbox.io/s/p7jj9k977x

 

  • Like 4
Link to comment
Share on other sites

59 minutes ago, Carl said:

Imagine going back in time a few years and opening up a fresh new VHS tape. If you put the VHS tape in the VCR and pushed the rewind button what would happen? nothing. You would need to play forward in order for it to rewind. Same thing applies to what you are doing.

 

Love this analogy! I'm wondering if it would work with young people.

  • Thanks 1
  • Haha 3
Link to comment
Share on other sites

1 hour ago, Carl said:

 


 methods: {
    play() {
      console.log("Clicked play")
      this.$refs.Logo.animateLogo()
    },
    reverse() {
      console.log("Clicked reverse")
      this.$refs.Logo.animateLogo().progress(1).reverse() //better
    }
  }

 

demo here: https://codesandbox.io/s/lrzp0p817q

 

@Carl Thank you for your excellent explanation. Could it be that your link leads to a wrong codeSandbox? The code you mention in the post is not in the example...

Upon inserting it myself I of course see that it works like a charm!

  • Like 1
Link to comment
Share on other sites

@OSUblake fantastic! Thanks a lot!

But I wonder what would happen if I had to something like that for 50 timelines or so, in many more than just one child component? I followed your example and tried some variants, but either the `this` context gets messed up, or the new var gets reactive, or if a new var is defined during a life cycle method, then the DOM is not available...

It is not easy to sync animations between and within child components from the root parent is, it seems to me.  
It would be great to have a structure like this, where all animation methods are saved to: (fake code, not working)
 

methods() {
  allAnims(refs) {
      logoAnimation = refs.Logo.animateLogo();
      sidebarAnimation = refs.Sidebar.animateSomething();
      navMainAnimation = refs.NavMain.animateMenues();
  },
  play() {
      this.allAnims.logoAnimation.play();
  },
  reverse() {
      this.allAnims.logoAnimation.reverse();
  }
},
mounted: {
	this.allAnims(this.$refs)
}


Or even call the method with

this.allAnims.animateLogo.progress(1).reverse(0)


But my attempts fail... 

Any thoughts?

Link to comment
Share on other sites

1 hour ago, Robert Wildling said:

But I wonder what would happen if I had to something like that for 50 timelines or so, in many more than just one child component? I followed your example and tried some variants, but either the `this` context gets messed up, or the new var gets reactive, or if a new var is defined during a life cycle method, then the DOM is not available...

 

 

Working with 50 timelines wouldn't be hard. I think the problem you might be having is wrapping your head around "this", and that's not uncommon. Everybody struggles with "this", and I do mean everybody.

 

You should look at what "this" is in your component. Vue creates a flattened object from your component code. 

 

console.dir(this);

 

 

1 hour ago, Robert Wildling said:

Or even call the method with


this.allAnims.animateLogo.progress(1).reverse(0)

 

 

 

You're not calling the allAnims method. A call will have parenthesis after it. It might help if you view your methods without the ES6 syntax. Methods are functions, and you can't add a dot (chaining) after calling a function unless something is returned.

 

Adding your animations to "this" allows you to access them from other methods. 

 

methods: {
  allAnims: function(refs) {
    this.logoAnimation = refs.Logo.animateLogo().pause();
  },
  play: function() {
    this.logoAnimation.play();
  },
  reverse: function() {
    this.logoAnimation.reverse();
  }
},
mounted: function() {
  this.allAnims(this.$refs);
  
  // WORKS
  this.play();
}

 

 

https://codesandbox.io/s/3vrnv37rvm

 

 

  • Like 6
Link to comment
Share on other sites

48 minutes ago, OSUblake said:

Everybody struggles with "this", and I do mean everybody.

 

That's odd. I've never once had any problems understanding "this". ?

 

It is a weird thing to wrap your head around. I bumped into this post a few months ago and bookmarked it. It's a good read.

https://tylermcginnis.com/this-keyword-call-apply-bind-javascript/

  • Like 2
Link to comment
Share on other sites

Aloha - the Green Sock has again provided fantastic help and insightful solutions! Millions and millions of thanks! 

 

I am happy to say that I eventually found to a similar solution to what you did, @OSUblake. I am not so sure, if separate play and reverse functions are necessary, though, here is my thougth.

Once this.logoAnimation is saved to Vue's method scope, I can call this.animation.play() and ...reverse() directly instead if this.play(). It saves some function definitions a la long... (pause() is great! I removed the play() method in the tween functions).

 

 Am I wrong? Or is this approach ugly? Or just a matter of taste?

BTW: I put my methods (or the method call to the initAnims()) on enter and appear lifecycle hooks. Otherwise, my setup would throw an error, when called from a subpage directly...

Good night! And millions of thanks again for your excellent help!!

Link to comment
Share on other sites

3 hours ago, Robert Wildling said:

I am not so sure, if separate play and reverse functions are necessary, though, here is my thougth.

My thought: once this.logoAnimation is saved to Vue's method scope, I can call this.animation.play() and ...reverse() directly instead if this.play(). It saves some function definitions a la long... (pause() is great! I removed the play() method in the tween functions).

 

 Am I wrong? Or is this approach ugly? Or just a matter of taste?

 

It's good. That's what I would do. ?

 

 

  • Like 1
Link to comment
Share on other sites

8 hours ago, PointC said:

I bumped into this post a few months ago and bookmarked it. It's a good read.

https://tylermcginnis.com/this-keyword-call-apply-bind-javascript/

 

Nice find!

 

8 hours ago, PointC said:

That's odd. I've never once had any problems understanding "this". 

 

Let me tell you about "that".

 

Before arrow functions came along, using "this" was very error prone. "this" inside an inner function is the wrong "this", which is very easy to overlook.

 

var myObject = {
  colors: ["red", "blue", "green"],  
  doCoolStuff: function() {
        
    this.colors.forEach(function(color) {
      this.logValue(color); // ERROR
    });
    
    return this;
  },
  logValue: function(value) {
    console.log(value);
  }
};

myObject.doCoolStuff().doCoolStuff();

 

 

To fix it you would have to put "this" in a variable. I would always name it "that", resulting in code that confused everybody.

 

Q. What's that?

A. this.

Q. What?!?

A. It's this!

Q. I don't know what that is!

 

var myObject = {
  colors: ["red", "blue", "green"],
  doCoolStuff: function() {
    
    var that = this;
    
    this.colors.forEach(function(color) {
      that.logValue(color); // WORKS
    });
    
    return this;
  },
  logValue: function(value) {
    console.log(value);
  }
};

myObject.doCoolStuff().doCoolStuff();

 

 

  • Like 5
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...