Jump to content
Search Community

React - Sharing timeline throught multiple components

Kovsky test
Moderator Tag

Recommended Posts

Hi guys,
I am currently rebuilding my website and i am asking myself what is the bestway to share one timeline through multiple components ?
let's say i have this architecture

--Layout

----Header

----Component1

--------Component2

--------Component3

----Footer

 

And i want part of my header timeline starting after the one executed in component3 ?

 

Currently i am using context but it feels a bit strange to me 

Provider.js

const [globalTimeline, setGlobalTimeline] = useState(
    gsap.timeline({ paused: true })
) // Global Timeline for animation
<myContext.Provider
value={{
 
globalTimeline,
setGlobalTimeline: tl => setGlobalTimeline(tl),
}}
>
{children}
</myContext.Provider>

 

Layout.js

useEffect(() => {
globalTimeline.addLabel('start'')
 
globalTimeline.play()
}, [])

 

I call my context in every component and add my animation to the globalTimeline.
Thank you

 

 
Link to comment
Share on other sites

Well, if you're using the context API, any component down the tree can consume that and use the regular GSAP methods to start that particular timeline. Keep in mind that you'll be passing a reference of the timeline, so it shouldn't mean any major memory consumption.

 

Now normally when I'm facing a situation like that I use redux since is more intended for such things, meaning you can update a state property in the store and trigger the timeline to be played based on that specific update, which can be followed with a useEffect hook. One thing that has to be handed to the hooks API is that using redux is far more simple and intuitive that it used to be. Recently I worked on a menu in a Next app that, because of the design, had the menu button in a different component from the navbar and a different component from the actual menu. So the button controlled it's own animation as well as the animation of the menu using redux, and when choosing a menu element, the same state property controlled the menu animation and the button animation. I'll try to create a live sample of it tomorrow at some time, but hopefully you get the gist of it.

 

Happy Tweening!!!

  • Like 4
Link to comment
Share on other sites

Hi,

Thank you for your answer. Just to be sure to clearly understand you.  I can go in a file and do something like this

contextTimeline.addlabel("one-animation").to(.....) 
and in another one

contextTimeline.from("#anId", {opacity: 0}, "one-animation-=2")

And another thing i was asking to myself, do i have to update this context timeline with something like this, everytime i add another part of the animation (samet thing if i use redux)

setContextTimeline(contextTimeline)

 

 

Last night i went crazy and i splited my timelines, one masterlayout timeline, onemaster tl for index etc....

Here is an exemple for my layout's animations https://github.com/ptrkvsky/blog-v2/tree/master/src/animation/layout

and here where i build it. https://github.com/ptrkvsky/blog-v2/blob/master/src/components/Layout.jsx

Does it seems a good way to do it too ?

But i will consider using redux for this, i use the old version at work (3.0) it will be a good way to test those new features.

 

Thank you again.

PS : It would be so nice to get some github repos with simple websites with good practices for React/Gatsby/Next/Nuxt

Link to comment
Share on other sites

Mhh... Ok this is a bit different from the original question. I thought this was about just controlling a timeline in different components, but this is a different territory.

 

Yeah this situations can be a pain in the neck actually because, while is ideal to keep components as small/simple as possible, some times we run into this issues where we need a specific DOM element in a grand parent component with the purpose of creating an animation or doing something else.

 

In this case I wouldn't use the context API at all for such purpose and try to tailor a solution for your specific scenario. If your component tree has three levels (as you mentioned in your first post):

23 hours ago, Kovsky said:

--Layout

----Header

----Component1

--------Component2

--------Component3

----Footer

I'll assume that the entire timeline is being created and started in the Layout or Header component. What I would do is to use a method in the Layout component in order to access the DOM elements and add them to the timeline, and Forwarding Refs in Component1 to get the refs from Component2, because (as you already found out ;)) sharing things between sibling components can be a bit tricky. In the layout component it would look a bit like this:

import { useRef } from "react";
import Header from "./components/Header";
import Component1 from "./components/Component1";

const Layout = () => {
  const getChildTweens (tween) => {
    // In here run some code to add the GSAP instances
    // in the main timeline
  };
  return (<div>
    <Header getChildTweens={getChildTweens} />
    <Component1 getChildTweens={getChildTweens} />
  </div>);
};

In the header component

import { useRef, useEffect } from "react";
import gsap from "gsap";

const Header = ({ getChildTweens }) => {
  const headerTween = useRef(null);
  useEffect(() => {
    headerTween.current = gsap.to(/* Header Animation */);
    // You can add other parameters to include labels, etc.
    // Now you sent the header tween to the layout component
    getChildTweens(headerTween.current);
  }, []);
  return(/* JSX in here */);
};

In Component 1:

import { useRef, useEffect } from "react";
import Component2 from "./Component2";
import Component3 from "./Component3";

const Component1 = ({ getChildTweens }) => {
  const component3Tween = useRef(null);
  const component3Ref = useRef(null);
  useEffect(() => {
    component3Tween.current = gsap.to(/* Header Animation */);
    // You can add other parameters to include labels, etc.
    // Now you sent the header tween to the layout component
    getChildTweens(component3Tween.current);
  }, []);
  return(<div>
    <Component2 />
    <Component3 ref={component3Ref} />
  </div>);
};

Finally in Component 3

import React, { forwardRef } from "react";

const Component3 = forwardRef( ({ ref }) => {
  return(
    // Here you use the ref passed from the parent component
    // where you need it
    <div ref={ref}></div>
  );
});

That is actually the 100% React Way of doing this and is not exactly super simple and easy to follow, but not all that complicated

 

Another alternative would be to use redux (or a custom store object) to store references to each GSAP instance and access them in the layout component in order to create the main timeline in the layout component and run it there. To be completely honest I've never tried something like that, but it should work.

 

Happy Tweening!!!

  • Like 3
Link to comment
Share on other sites

  • 8 months later...

Rodrigo -

 

Using this method, do you need to pass the timeline/tween up with a different function callback for each component?  I know in your example you used the same function for each component "getChildTweens", but won't it be overwritten for each timeline so you'll be left with just the final timeline from your last child component?  If not, how do you differentiate between each timline/tween that you pass up to the parent?  In my case, I'm passing up timelines, and in your example you're passing up Tweens, so maybe that's the difference since those are triggered as they come through.

Link to comment
Share on other sites

  • 1 year later...
On 9/27/2020 at 10:33 AM, Rodrigo said:

Well, if you're using the context API, any component down the tree can consume that and use the regular GSAP methods to start that particular timeline. Keep in mind that you'll be passing a reference of the timeline, so it shouldn't mean any major memory consumption.

 

Now normally when I'm facing a situation like that I use redux since is more intended for such things, meaning you can update a state property in the store and trigger the timeline to be played based on that specific update, which can be followed with a useEffect hook. One thing that has to be handed to the hooks API is that using redux is far more simple and intuitive that it used to be. Recently I worked on a menu in a Next app that, because of the design, had the menu button in a different component from the navbar and a different component from the actual menu. So the button controlled it's own animation as well as the animation of the menu using redux, and when choosing a menu element, the same state property controlled the menu animation and the button animation. I'll try to create a live sample of it tomorrow at some time, but hopefully you get the gist of it.

 

Happy Tweening!!!

@Rodrigo I need a master timeline for my homepage. Because the homepage itself is split into multiple components, a master timeline would be really helpful in handling how to sequence the animations. That's why I was trying to store a timeline as a global state. I went ahead with redux and tried using redux tookit to store a gsap timeline as a state like you suggested in this post but am running into the error "Too much recursion"

 

import { createSlice } from "@reduxjs/toolkit";
import gsap from "gsap";

const initialState = gsap.timeline();

const timelineSlice = createSlice({
  name: "timeline",
  initialState,
  reducers: {},
});

export default timelineSlice.reducer;

This is what my timelineSlicelooks like, any suggestions on what to do? I also saw a globalTimelinein the docs can that be of any help here?

Link to comment
Share on other sites

Hi @Ishan Shishodiya,

 

It seems that you also created a new thread for this:

Do you mind if we keep the discussion in that thread 👆 instead of extending this one? Since you're using redux the scenario is different than the one in this thread, so it'd be better to focus our attention in your other thread 👍

 

Happy Tweening!

  • Like 1
Link to comment
Share on other sites

  • 11 months later...
On 6/10/2021 at 3:02 PM, Steve Giorgi said:

Well, the answer to this is simple enough.  You can create an ref in the parent component and push each timeline into it for use in a master timeline. 

 

If anyone runs into this please let me know and I'll boil the code down and post an example.

could you help me with that?

Link to comment
Share on other sites

Hi @Zeus_,

 

Without a minimal demo, it's very difficult to troubleshoot; the issue could be caused by CSS, markup, a third party library, a 3rd party script, etc. Would you please provide a very simple CodePen or Stackblitz that illustrates the issue? 

 

Please don't include your whole project. Just some colored <div> elements and the GSAP code is best. See if you can recreate the issue with as few dependancies as possible. Start minimal and then incrementally add code bit by bit until it breaks. Usually people solve their own issues during this process! If not, at least we have a reduced test case which greatly increases your chances of getting a relevant answer.

 

See the Pen aYYOdN by GreenSock (@GreenSock) on CodePen

that loads all the plugins. Just click "fork" at the bottom right and make your minimal demo

 

Using a framework/library like React, Vue, Next, etc.? 

CodePen isn't always ideal for these tools, so here are some Stackblitz starter templates that you can fork and import the gsap-trial NPM package for using any of the bonus plugins: 

 

Please share the StackBlitz link directly to the file in question (where you've put the GSAP code) so we don't need to hunt through all the files. 

 

Once we see an isolated demo, we'll do our best to jump in and help with your GSAP-specific questions. 

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...