Jump to content
GreenSock

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

Animate dynamically elements with React Hooks

Recommended Posts

Hi there,

 

I'm new with gsap, and I would love to integrate it with my react projects.

 

For what I understood, to use gsap to animate my elements, I need first to target them with ref and useRef(), I was wondering in case I need to create a grid based on data coming from an api call, let's say a not definied number of tiles with images, and I want them to appear with a staggered animation, how should I proceed ? 

I am a bit confused because I don't know how to target them with useRef() 

 

I hope that my question makes sense, in case I'm happy to clarify

 

Cheers

Link to post
Share on other sites

Hi and welcome to the GreenSock forums.

 

First, thanks for becoming a GreenSock club member and supporting GSAP!!!

 

The way that I would go about animating a set of elements that depends on a dynamic data would be to store the data in the state and the useEffect() hook to create the animation when the state is updated. Here is a very simple example illustrating that:

 

https://codesandbox.io/s/gsap-stagger-dynamic-elements-2d20l?file=/src/Users.js

 

Now, considering that you're asking about image tiling the approach could be different but similar in some way. For example you can store in the state the amount of tiles you want to generate, create the tile elements and store them in an array and finally create the GSAP instance and animate it.

 

Finally keep in mind that GSAP Staggers are quite amazing and there is an extremely wide range of possibilities so you can create really amazing animations, check it out:

 

https://greensock.com/docs/v3/Staggers

 

Happy Tweening!!!

  • Like 3
Link to post
Share on other sites

Thank you Rodrigo, that explains perfectly what I wanted to do, I'll try to implement it, and see how it goes. 

 

Cheers

  • Like 1
Link to post
Share on other sites

Considering your sandbox example, is there a way to have those animating elements showing when the page is loaded instead of a click trigger.

 

The problem that I am facing mainly is due to the fact that useEffect runs before gsap can get a reference and I will be yelled that gsap can't read a null obj.

 

  useEffect(() => {
 
  const callApi = async () => {
 
  let data = {
    category: "MEMORY",
    numberOfRecords: 10,
   };
 
  const responseData = await API.getData(data);
 
  //if the data is available it will go in the state and animation will be triggered.
  if (responseData) {
 
     setData(responseData.response.data.data.data);
 
     const tl = gsap.timeline({ paused: true });
     tl.to(usersDomNodes, {
        duration: 0.3,
        stagger: 0.05,
        y: 0,
        autoAlpha: 1,
     }).play();
    }
  };
   //call the api
    callApi();
 
  }, []);

 

Thank you,

 

Andrea

Link to post
Share on other sites

Mhh... I assume that setData() is the setter you're using with useState() in order to update the data state property, right? Also I'll assume that usersDomNodes are updated when the state is updated.

 

If that is the case, it could be possible that by the time the timeline instance is created the usersDomNodes array is empty. Keep in mind that in my sample the whole idea of the buttons is to mimic a server response, that's why I create and run the timeline instance after the particular state property is updated. While the initial useEffect() is actually the best place to create your API call, it could not be the best place to create the GSAP instance to animate the elements that depend on the API callback.

 

Could you provide a simple live sample so we can take a better look? That would speed up the debugging process.

 

Happy Tweening!!!

  • Like 3
Link to post
Share on other sites

Thank you again for your answer, I hope you had a great weekend.

 

I have made a Sample, I left the animation function outside the useEffect, and called it after the data has been set to the state, but evidently I am missing something.

 

I hope now it'll be easier to understand what I'm doing and where I'm not doing the right right thing.

 

cheers

 

Andrea

 

p.s. just realized I've used material-ui,  assumed that was not a problem 😅

Link to post
Share on other sites

Hi,

 

I'm not completely clear on how you're planning to do this animations, but there are a few things wrong with your code.

 

First, you're creating this timeline:

const tl = gsap.timeline({ paused: true });
tl.to(usersDomNodes, {
  duration: 0.3,
  stagger: 0.05,
  y: 0,
  autoAlpha: 1
}).play();

That basically takes the elements from the current Y position to zero and from the current opacity to 1. The problem is that when your animation starts, the Y position of those elements is zero and their opacity is 1, so GSAP takes the elements and animates them from opacity 1 to 1, so nothing happens. You have to set the initial style of the elements to some Y position and opacity zero:

cardHover: {
  maxHeight: "250px",
  padding: "10",
  margin: "5px",
  border: "1px solid #101010",
  borderRadius: "10px",
  borderTop: "none",
  // add these sytles to make them hidden
  opacity: 0,
  visibility: "hidden",
  // add this to create the animation in the Y axis
  transform: translateY(-25px),
  marginBottom: 10,
}

Second, as I mentioned before create the animation as an effect of the state being updated, not as a callback of the API response, right now GSAP is complaining that you're passing an array of null elements to animate, therefore nothing will happen, even if you set the initial styles properly. This code actually works:

useEffect(()=>{
  const tl = gsap.timeline({ paused: true });
  tl.to(usersDomNodes, {
    duration: 0.3,
    stagger: 0.05,
    y: 0,
    autoAlpha: 1
  }).play();
}, [data]);

Just remove the runAnimation method from the component and the reference in the API callback.

 

Third, you're using async/await without the await statement in the API call and without a try/catch block, if for some reason the API call fails, you're app will have an unhandled promise rejection and you won't know what the problem is:

const getUsers = async () => {
  try {
    // for this sample I'm using axios
    const myUsers = await axios.get("domain.com/users");
    // now update the state
    setData(responseData);
  } catch (err) {
    console.error(err);
    // perhaps do something else, some UI notification of an API error
  }
}

Finally, perhaps you should use some code to check that the images are loaded in order to start the animation after all the images are loaded, otherwise the animation runs on the empty containers.

 

Happy Tweening!!!

  • Like 3
Link to post
Share on other sites

I guess, I should have been clearer with what I wanted to achieve.

 

Basically I want to have some sort of animation once I land on the page, items to be animated require data coming from an api call, so I was not sure how to trigger the animation, since it should start as soon as the the page has finished the rendering.

 

Now thanks to your suggestrions, especially the last one I've discovered the onLoad attribute and with that I'm able to trigger the animation once the images are loaded, and works as desired.

 

Sorry for the confusion 😅

Link to post
Share on other sites

No worries, everyone is here to learn, the only difference is that some of us have been around more time, that's all ;)

 

Happy Tweening!!!

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

×