Jump to content
GreenSock

Search In
  • More options...
Find results that contain...
Find results in...
Kovsky

React - Sharing timeline throught multiple components

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 post
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 post
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 post
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 2
Link to post
Share on other sites

Thank you with this very detailed answer !

I will go with this option.
Have a good sunday

Link to post
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.

×