Jump to content
Search Community

Using gsap.utils.toArray in React

FarhanSU test
Moderator Tag

Recommended Posts

Hello, I hope you all are doing well. I am becoming a little more familiar with GSAP and read about the gsap.utils.toArray which would be useful for animating multiple items and I was wondering how I would use this in React. I gave it shot trying to make it work, but for me it only worked on one element. I also had trouble setting up this on codepen. I originally had const st = useRef(ScrollTrigger.defaults({toggleActions: "restart reset restart reset"})); but on Codepen it says ScrollTrigger.defaults is not a function. The rest of the code are identical to what I wrote. I am using this codepen as a reference: 

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

 

 

I would really appreciate on any advice on how to approach this! (This one below is mine)

See the Pen RwrBKVM?editors=0010 by FarhanSU (@FarhanSU) on CodePen

Link to comment
Share on other sites

18 hours ago, GreenSock said:

You're loading the wrong ScrollTrigger - the one you loaded is from a completely different library. :) 

 

BADhttps://cdnjs.cloudflare.com/ajax/libs/ScrollTrigger/1.0.4/ScrollTrigger.min.js

GOODhttps://cdnjs.cloudflare.com/ajax/libs/gsap/3.4.1/ScrollTrigger.min.js

I am sorry again!

This is the link to a working one: https://codesandbox.io/s/suspicious-leaf-up3l7?file=/src/App.js

Link to comment
Share on other sites

9 hours ago, ZachSaucier said:

It's not working - it still has a lot of errors for me.

I am really sorry once again. I accidentally used the same code sandbox for another project. I have replicated the issue I am having in this. Which is when using gsap.utils.toArray to animate multiple elements (the blue and the red text in this case) with the same ref, but the scroll trigger animation only works for one of the element (the red text only.)

 

The working codebox: https://codesandbox.io/s/suspicious-leaf-up3l7?file=/src/App.js

I am sorry once again for my ignorance, I appreciate any help because I am struggling to figure this out.

 

 

Link to comment
Share on other sites

Hi, I am on my mobile so can't really write much code but I belive you just need an array of refs for this to work. Currently you are only animating the last item because that what the anim.current is.

 

Checkout this example on how to create an array or refs in React and use them with ScrollTrigger.

 

https://ihatetomatoes.net/react-and-greensock-tutorial-for-beginners/#5-how-to-create-an-array-of-refs

 

Let me know if that helped.

 

  • Like 3
Link to comment
Share on other sites

7 hours ago, Ihatetomatoes said:

Hi, I am on my mobile so can't really write much code but I belive you just need an array of refs for this to work. Currently you are only animating the last item because that what the anim.current is.

 

Checkout this example on how to create an array or refs in React and use them with ScrollTrigger.

 

https://ihatetomatoes.net/react-and-greensock-tutorial-for-beginners/#5-how-to-create-an-array-of-refs

 

Let me know if that helped.

 

Hellp, thank you for you response. I have tried this earlier on one of my other pages. Thank you for writing this article, it did help. For this particular page, I was hoping for more of a dynamic approach without having to hard code them before, so I can use images, or link elements to another page, and etc.

 

Is there any way to do that? Say even creating multiple refs and/or one trigger ref to be used multiple times on different elements?

Link to comment
Share on other sites

That's exactly what the array of refs is used for.

 

You can loop over all the elements inside of each loop and create one ScrollTrigger that will be applied to all elements, but for each element individually. 

 

Are you trying to avoid writing ref="something"?

 

So if gsap .toArray worked wouldn't you still need to give your elements a specific class? className="something"?

  • Like 2
Link to comment
Share on other sites

On 7/19/2020 at 5:04 PM, Ihatetomatoes said:

That's exactly what the array of refs is used for.

 

You can loop over all the elements inside of each loop and create one ScrollTrigger that will be applied to all elements, but for each element individually. 

 

Are you trying to avoid writing ref="something"?

 

So if gsap .toArray worked wouldn't you still need to give your elements a specific class? className="something"?

Thank you @Ihatetomatoes for your help again, and I apologize for the late reply. I meant avoid using the map function, if i could use the same ref that that would be perfect so I can style each element seperately. I tried using classname but it does not work.

 

Aside from gsap.utils.toArray, I discovered the the Scroll Trigger Batch method. I gave it a shot trying to implement it in React through useEffect. I have tried following this gsap tutorial on .batch

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

 

On my app, It didn't work using the class trigger but using an useRef const worked only on one of the element even tho i set the ref to each element. What am I doing wrong?

 

This is my codepen: https://codesandbox.io/s/scrolltrigger-batch-dijll?file=/src/App.js

I added some extra content, and if you scroll down you would be able to see the batched section. I am not getting the error on the codepen but i do get an errorTypeError: element.getBoundingClientRect is not a function

and it is pointing at the scrolltrigger batch. I fully understand I can not just add ScrollTrigger.Batch but I do not know how to do it otherwise.

 

Thank you very much in advance!

Link to comment
Share on other sites

If I understand your question correctly, this has to do with the fact that timelines must wait 1 tick before doing their initial refresh because if they did it immediately upon creation, they'd be totally empty and there wouldn't be any duration (you haven't populated it with tweens yet), thus when you do your batch() it refreshes BEFORE your timeline-based one which has a pin. The pin essentially adds extra space and pushes things down, so the start/end positions from your batch() haven't accounted for that. 

 

The solution should be simple: just add a ScrollTrigger.refresh() right after your .batch() call to manually trigger it.

 

Does that help? 

  • Like 1
Link to comment
Share on other sites

On 7/24/2020 at 11:50 PM, GreenSock said:

If I understand your question correctly, this has to do with the fact that timelines must wait 1 tick before doing their initial refresh because if they did it immediately upon creation, they'd be totally empty and there wouldn't be any duration (you haven't populated it with tweens yet), thus when you do your batch() it refreshes BEFORE your timeline-based one which has a pin. The pin essentially adds extra space and pushes things down, so the start/end positions from your batch() haven't accounted for that. 

 

The solution should be simple: just add a ScrollTrigger.refresh() right after your .batch() call to manually trigger it.

 

Does that help? 

Thank you the advice. I added a different varible for a different timeline since I do not want the timeline with the batch to have a pin. I really just want a simple y: 100 and fade in box which i was assuming what theonEnter: batch => gsap.from(batch...)

did. Is there anywhere else I should add a gsap.to/gasp.form? On the codepen, I have added the Scrolltrigger.refresh() right after my batch calls however, I must be doing something wrong because I made the container height bigger, but none of the other boxes shows up and I do not know if using the same ref is on each div box is working. Would I need to set a trigger on my batch, I have tried that setting the batch ref to the container but that did not work?

 

I apologize for my ignorance I am still struggling with this method, but since one of my page on my app is going to have many boxes, I think it would be more efficient to use batch or even arrays (which I am also clueless about) than individually writing a ref for each box . Thank you for all you guys help! I really appreciate it :)

Link to comment
Share on other sites

29 minutes ago, FarhanSU said:

On the codepen, I have added the Scrolltrigger.refresh() right after my batch calls

I don't see that. Are you sure? 

 

I'm not sure I follow your question. The CodeSandbox you provided seems to work the way I'd expect, especially after adding ScrollTrigger.refresh(); after your batch() call. What am I missing? Can you please provide a minimal demo (if it's not the one you previously provided)? 

Link to comment
Share on other sites

1 hour ago, GreenSock said:

I don't see that. Are you sure? 

 

I'm not sure I follow your question. The CodeSandbox you provided seems to work the way I'd expect, especially after adding ScrollTrigger.refresh(); after your batch() call. What am I missing? Can you please provide a minimal demo (if it's not the one you previously provided)? 

Yup, it is still this demo: https://codesandbox.io/s/scrolltrigger-batch-dijll?file=/src/App.js

The ScrollTrigger.refresh is on line 66. The problem I was talking about is that, the batch only renders one box, and not the other 9 boxes i wrote. Scrolling past the elements, only the first box appears. I am not sure why the other boxes are not showing up?

 

Thank you once again!!

Link to comment
Share on other sites

5 hours ago, FarhanSU said:

the batch only renders one box, and not the other 9 boxes i wrote. Scrolling past the elements, only the first box appears. I am not sure why the other boxes are not showing up?

console.log(agencyReveal) and see what it gives you - it's just the one element. Fix your ref to refer to all of your boxes and it should work.

Link to comment
Share on other sites

6 hours ago, ZachSaucier said:

console.log(agencyReveal) and see what it gives you - it's just the one element. Fix your ref to refer to all of your boxes and it should work.

Hello @ZachSaucier, I put the console log right before my return, and the console.log points the ref to

Object {current: HTMLDivElement}

current:

<DIV class="box" style="transform: translate(0px, 100px);"></DIV> 

I am not sure what to do with that information since its not telling me something is wrong. Should it have said array instead of an object? 😕

Link to comment
Share on other sites

11 hours ago, FarhanSU said:

Yup, it is still this demo: https://codesandbox.io/s/scrolltrigger-batch-dijll?file=/src/App.js

The ScrollTrigger.refresh is on line 66.

It wasn't there when I checked (maybe a caching thing?). Even now, it's incorrect - you literally have it inside your .batch() call as a parameter :) It belongs AFTER the .batch() call. 

 

But that has nothing to do with the fact that it's only handling one box. That's a React question. I have almost no experience with React, but from a quick Google search, it looks like you've gotta create your own Array and then add references like this:

 

// BAD:
<div ref={agencyReveal} className="box" />
<div ref={agencyReveal} className="box" />

// GOOD: 
const agencyReveal = []; 

<div ref={(el) => {agencyReveal.push(el)}} className="box" />
<div ref={(el) => {agencyReveal.push(el)}} className="box" />

 

https://codesandbox.io/s/scrolltrigger-batch-qxh0r?file=/src/App.js

 

Again, I could be wrong about the React stuff. You'll need to verify the React-specific stuff since these forums are really supposed to be about GSAP-specific questions. 

 

I hope that helps!

  • Like 1
Link to comment
Share on other sites

2 minutes ago, GreenSock said:

 


// BAD:
<div ref={agencyReveal} className="box" />
<div ref={agencyReveal} className="box" />

// GOOD: 
const agencyReveal = []; 

<div ref={(el) => {agencyReveal.push(el)}} className="box" />
<div ref={(el) => {agencyReveal.push(el)}} className="box" />

 

 

No, don't do that. If you're using regular variables inside React Hooks, you're probably doing it wrong.

 

And don't do this.

// BAD
const tl = useRef(gsap.timeline({ paused: false }));
const sl = useRef(gsap.timeline());

// GOOD
const tl = useRef();
const sl = useRef();

 

You can do this to get the agencyReveal elments.

agency.current.children

 

  • Like 1
Link to comment
Share on other sites

Thank you very much @GreenSock and @OSUblake for clarifying both the tl ref (which is good because I can fix that on the other pages) and the .children prop.

 

I am not sure why using ref={agencyReveal.current.children} on my box elements results in an error that says agencyReveal.current is undefined. I also tried using React.createRef but the issue is still there. I have updated my codepen here: https://codesandbox.io/s/scrolltrigger-batch-dijll?file=/src/App.js

 

However, the code written by @GreenSock using  ref={el => agencyReveal.push(el);}}  works

On 7/31/2020 at 4:23 PM, GreenSock said:

 

Thank you all very much for your patience, I appreciate it immensenly!

Link to comment
Share on other sites

20 minutes ago, FarhanSU said:

ref={agencyReveal.current.children}

 

That's not all what I meant.

const agency = React.createRef();

useEffect(() => {
  sl.current = ScrollTrigger.batch(agency.current.children, {
    ...
});
    
<div ref={agency} className="layerfour">
        <div className="box" />
                                   <div className="box" />
                                   <div className="box" />
                                   <div className="box" />
                                   <div className="box" />
                                   <div className="box" />
                                   <div className="box" />
                                   <div className="box" />
                                   <div className="box" />
      </div>

 

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