Jump to content
Search Community

GSAP + React - how to access timeline in children components

Gilbert 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

I am experimenting with these title animations https://github.com/bigfanjs/react-titles and I want to sequence some titles. 

The questions that I have are:

 

How can I pause or play the original animations?

How can I sequence the animations (for each title)? - For example, play the first one, wait for .7 seconds and play the second one, etc.

How can I set the initial position of each title?

 

My code is:

Here is the code I have:

import React, { Component, Fragment } from "react";
import styled from "styled-components";
import * as titles from "react-titles";
import { TimelineMax, Power3 } from "gsap";

const mytitles = [
  {
    component: "Title2",
    title: "This is ",
    subTitle: "Sweet",
    isOpen: false,
    size: 100,
    x: '50'
  },
  {
    component: "Title6",
    title: "Let' see",
    subTitle: "if this works",
    isOpen: false,
    size: 400,
    x: '400'
  },
]

class Title extends Component {
  constructor(){
        super();
        this.renderTitle = this.renderTitle.bind(this);
        this.restartHandler = this.restartHandler.bind(this);
        this.tl = new TimelineMax({paused: true});
    }

  state = {
    isOpen: false
  }

  restartHandler(){
        const {tl} = this;
        tl.reversed( !tl.reversed() );
    }

  componentDidMount(){
        const {tl} = this;

        mytitles.forEach( (e,i) => {
            //tl.set(this.refs[e.component], {left:e.x, top:e.x})
            tl.to( this.refs[e.component], 1, {x: e.size, y: e.size/3 , scale: 0.5, opacity: 0.5}, .5 * i );
        });
        tl.reverse();
    }

  getComponent(e) {
    const ATitle = titles[e.component];
    //ATitle.handleRest();

    //const {t1} = this;
    //this.tl.set(this.refs[e.component], {left:e.x, top:e.x})
    return(
      <div  className="box" key={e.component} ref={e.component} >
        <ATitle
          size={e.size}
          text1={e.title}
          text2={e.subTitle}
          open="false"
          style={{ position: "absolute", x: " +e.x+" , y: " +e.x+", "fill": "orange" }}
          onComplete={this.handleComplete}
        />
      </div>
    )
  }

  renderTitle(e,i){
        return(
            this.getComponent(e)
        );
    }

handleComplete = (status) => {
    /*const {selected, component} = this.state;

    if (!status && selected !== component) {
      this.setState({ component: selected, isOpen: true });
    }*/
  }

  render() {
    return(
        <div>
            <button onClick={this.restartHandler}>Toggle Tween</button>
            <div>
                {mytitles.map(this.renderTitle)}
            </div>
         </div>
   )
  }
}

export default Title;

 

Link to comment
Share on other sites

Hi Gilbert and welcome to the GreenSock forums.

 

First try to avoid string refs, they are deprecated since React 15.x. Use the ref callback instead:

 

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

 

In the constructor create an empty array to add the elements as you call the render title method:

 

// this is inside the component's class
constructor(){
  super();
  // just an empty array, we'll use the ref callback to add the DOM elements
  this.titles = [];
  // the rest of your constructor code here
}

 

Then, IMHO I don't see the need for two methods to render each title, you can run all your logic in just one and use the ref callback to add the element to the array, like that you'll have a predictable way to add your elements to the timeline.

 

renderTitle(e) {
  
    const ATitle = titles[e.component];
  
    return(
      <div  className="box" key={e.component} ref={e => this.titles.push(e)} >
        <ATitle
          size={e.size}
          text1={e.title}
          text2={e.subTitle}
          open="false"
          style={{ position: "absolute", x: " +e.x+" , y: " +e.x+", "fill": "orange" }}
          onComplete={this.handleComplete}
        />
      </div>
    )
  }

 

The key is the ref callback which has the DOM node as the argument, then the DOM node is added to the titles array. Then you can access the titles array and create your timeline:

 

componentDidMount(){
  const { tl, titles } = this;
  
  titles.forEach( (e,i) => {
    tl.to( this.refs[e.component], 1, {x: e.size, y: e.size/3 , scale: 0.5, opacity: 0.5}, .5 * i );
  });
  tl.reverse();
}

 

This is a better and more actual approach to write your app.

 

Now to the sequencing part of your app. I'm a bit puzzled, because react-titles does creates either a GSAP instance or uses react-motion for the animations. If you want to control those animations, you'll have to fork the repo and create an API that exposes some way to do what you need, because react-titles starts as soon as the component is mounted. For the initial position, what I saw in the repo is that the code uses fromTo instances, which create an immediate render, which sets the initial position of the elements being animated, so no matter what you do react-title will enforce those initial values passed in the props of the component's instance. Such is the nature of re-usable React components, you have to work with whatever the creator of the component is giving you.

 

Also keep in mind that the open prop passed to the component is a boolean and you're passing a string "false". Try using a boolean instead:

 

<ATitle
  size={e.size}
  text1={e.title}
  text2={e.subTitle}
  open={false}
  style={{ position: "absolute", x: " +e.x+" , y: " +e.x+", "fill": "orange" }}
  onComplete={this.handleComplete}
/>

 

Another option is hack into the code of react-titles and create a way to control the animations. Honestly if I was you, I'd take the animations being created by react-titles and bake my own solution for the particular needs of the project.

 

The GSAP code in react-titles is good and there are no issues there and unfortunately we can't spend a lot of time figuring out how GSAP based tools work and how they're used. I saw that you started an issue in the repo, hopefully the creator(s) of the tool can help you more than I can and they can add a way to keep the tweens paused when mounting and give more control over them, as well as creating more complex sequences.

 

Happy Tweening!!!

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

17 hours ago, Rodrigo said:

Ever heard of conditioning??

 

Ha! I'm the same way with SVG or mask related questions. I can't resist and start drooling. ? Blake will almost always appear when canvas and/or Pixi are mentioned in a thread. Everyone does seem to have their strong areas and favorites. Now if I can just get a PointC-SVG-Signal to show in the sky outside my window, I'd be all set. ;) 

  • Haha 2
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...