Jump to content
GreenSock

tomsah

GSAP + REACT, play/reverse animation onClick issue

Recommended Posts

Hello everyone,

First of all, thank you for this amazing API and incredibly useful forum.

I am learning GSAP, so I am a beginner. Please bear with me.

I am building a react application and I am trying to implement the animations with GSAP.

As you can see in a code pen, I have 4 panels that slide in and out of the screen onClick.

 

I kind of managed to do it but there is some issue with the reverse animation that I do not understand.
The issue is, more I play the animation more the reversed animation takes time to start.

I have made a bit of debugging and for that, I have use onComplete callback and the progress method to try to understand what goes wrong.
I have realised that every time the animation got played the onComplete function got call exponentially and the process method never goes from 0 to 1.
Please see the screenshot attached.

 

I think that may be the reason of issue but I do not know exactly why and I cannot find a solution for it.
It may be because of the way I use react and GSAp together or my way to use GSAP or anything else that I do not see.

 

Thank you in advance for your help
Happy tweening.
 

Screen Shot 2018-03-15 at 7.11.58 PM.png

See the Pen WzwWqb by tomsah (@tomsah) on CodePen

Link to comment
Share on other sites

Hi Tomsah and welcome to the GreenSock forums.

 

The issue here lies in the fact that you're creating a new instance of the timeline each time you click on the toggle button. The real kicker is that, since the timeline is on a global scope, everytime the slideMenu method is called, GSAP says: "ok, so your calling a method that adds this instances to the timeline and since you're not specifying where you want them, I'll add them to the end". So everytime you click on the toggle button the timeline gets longer and longer and longer and longer and... you see where this is going right?? ;-)

 

React has components lifecycle methods to help with this. You can create the timeline just once in the componentDidMount method and the timeline will be created and you can be sure that your elements are in the DOM when it happens. Just remove the call to slide menu in the toggle button click handler and add the component did mount call in your component:

 

// add component did mount to the class
componentDidMount(){
  this.slideMenu();
}

// remove the slide menu call in the toggle click handler
toggleMenu = (e) => {
  e.preventDefault();
  menuTl.reversed() ? menuTl.play() : menuTl.reverse();
  this.setState({
    menuActive: !this.state.menuActive
  })
}

 

On a related note, the more purist React developers will consider using selectors like this a big NO-NO:

 

var projects = document.getElementsByClassName('project');

 

React is all about doing things in a declarative way, so for getting dom nodes the so called "React way"  is to use refs which in some cases can be really painful, specially if you're working with components that you didn't wrote, which makes generating and accessing the refs a bit tricky. I've been using React with GSAP for some time now and this is just my opinion, but in the case of elements that are permanently rendered on the DOM and are always there, there's no issue in doing it like you did. If that's no the case, the one thing you have to keep in mind is if a state or props change could un-mount the component being animated or elements contained in that component that could be animated, because the component could be unmounted and the GSAP instance could be still be active and you'll get an error. In that case you can use componentWillUnmount to kill the animation and prevent an error.

 

Happy tweening!!

  • Like 5
  • Thanks 1
Link to comment
Share on other sites

@Rodrigo Thank you so much for the fast answer, it is making sense to me.
I will try to implement your solution and get back to you with the result a bit later today.

Happy tweening

  • Like 1
Link to comment
Share on other sites

@Rodrigo 
Thanks so much for this very detailed and complete answer, moving the call of the function in the componentDidMount resolved the problem.
I am still getting my head around the why and how works that way. I am not very experienced with React and GSAP yet.

I updated the codepen but still looking at the refs situation.

I have a few more questions to help me understand how is it all working together? if you have the time. :)


- is it bad practice to declare my TimelineMax outside of the class component in a global scope as I did? if yes where is it best?

- is the staggerTo method is the best approach to realise my animation, I was hesitating to use a forEach and tweenMax.to with a delay?

- would it be better to use react-transition or something similar to deal with GSAP and react? It seems working well without anything else.

regarding the ref
I have been using them when I could in the application that I am building but always in the same component.
however, in that precise case, the projects section is a component on its own (child 2 level down) and I have the animation function at the top level of the app.
I just don't know how to access a child ref in a parent. but looking at it at the moment, any tips will be welcome.                                            Sorry, It got a bit long. 
Again thank you very much
It may take a month or two but I will come and post the link of the finished website.

happy tweening

Edited by tomsah
forgot something
Link to comment
Share on other sites

Hi
 

Unfortunately I ran out of time to get a proper answer, I'll get back to it later tonight.

 

In the mean time take a look at this:

https://reactjs.org/docs/refs-and-the-dom.html

 

This:
 

And this:
 

See the Pen aYZexp?editors=0010 by rhernando (@rhernando) on CodePen

 

Hopefully this is enough for now.

 

Happy Tweening!!

  • Like 4
Link to comment
Share on other sites

@Rodrigo
 thank you for the pointer, I am trying to understand it all, a bit confusing at the moment.

Happy Tweening!!

Link to comment
Share on other sites

Hey Tomsah, getting back to it now.

 

Quote

- is it bad practice to declare my TimelineMax outside of the class component in a global scope as I did? if yes where is it best?

 

Not at all, your GSAP instance will be available to every method in the global scope of your module or file. Some people like to attach the GSAP instance to the react component like this:
 

import React, { Component } from "react";
import { TimelineLite } from "gsap";

class MyApp extends Component {

  constructor(){
    super();
    this.projectTl = new TimelineLite({paused: true, reversed: true});
  }
  
  render(){
  	return(...);
  }
           
}

 

This allows to pass some of the components props or state properties to GSAP's callback system and stuff like that or if you need to reference a specific method from your component in a callback, or perhaps update the state using onComplete, onRevereseComplete or something like that.
 

Quote

- is the staggerTo method is the best approach to realise my animation, I was hesitating to use a forEach and tweenMax.to with a delay?

 

I don't see anything wrong with using stagger to do that. Keep in mind though that the stagger methods return a series of GSAP instances with different position parameter applied to them, based on the stagger time you provide.

 

Quote

- would it be better to use react-transition or something similar to deal with GSAP and react? It seems working well without anything else.

 

I recommend using React transition group only when the animation is related to the component being mounted/unmounted. For example if you want the element to be mounted before the animation starts and unmounted after the animation is complete, or if you're looking for some finer detail control over the lifecycle methods and associate them to the animation, you can use transition group. Other wise my advice is to stick with GSAP and use it in he usual way with react. Transition group might be a little too much if you're just starting with react and GSAP.

 

16 hours ago, tomsah said:

I have been using them when I could in the application that I am building but always in the same component.
however, in that precise case, the projects section is a component on its own (child 2 level down) and I have the animation function at the top level of the app.

 

If the animation only affects the elements in that component, then there's no reason to have it in a different component. If you want to target some specific callback from a parent component in the timeline, then you can pass it as a property. Finally if you need to access the GSAP instance in different components in your app, perhaps you should think about using redux, mobx or some other store solution in order to access the timeline in different components. Now I have never used redux to store a GSAP instance, but I can't think about a reason against though.

 

Happy Tweening!!!

  • Like 5
Link to comment
Share on other sites

@Rodrigo
That is an amazing answer, could not wish for better.
Many thanks for the help and guidance.
GSAP is an incredible community and I am really happy I came and ask questions.
I'll come back and post a link to the finished website as soon as it is done.

 

Happy Tweening

  • Like 1
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.
×