Jump to content
GreenSock

SteveS

React Flip w/ conditional rendering

Go to solution Solved by GreenSock,

Recommended Posts

I've seen the documentation and examples shown in the Advanced GSAP + React post, but I don't understand why this isn't working. The effect is extremely simple in framer-motion: https://codesandbox.io/s/framer-motion-layout-animations-snxgv?from-embed=&file=/src/App.tsx

 

And my attempt to recreate it with GSAP:
https://codesandbox.io/s/distracted-meninsky-n0xjey?file=/src/App.js

 

This is unbelievably easy in framer motion, but I'd prefer if I didn't have to include a whole library for a few basic effects. The code seems to make sense. Save the flip state in react state, when re-render occurs, FLIP to that state and save the new state. Any ideas?

See the Pen App.js by s (@s) on CodePen

Link to comment
Share on other sites

The CSB embed has a 404 on my end, but the link appears to work fine.

Link to comment
Share on other sites

Apologies for the mess, took a while to get my head around this.

I'm not a react person so we'll likely be best at figuring this out between the two of us rather than me continuing to hack away.

So the underline wasn't animating because it's not the same element getting moved to a new place in the DOM, it's being removed entirely and then a new element is being created. If you add an entrance animation you can see that it's being added to the DOM each time.

https://codesandbox.io/s/crazy-cohen-n9jky0?file=/src/App.js

What I would suggest, (maybe?) is to keep the underline in the DOM but bump it out a level into the main 'tabs' div. Then add a current class to whichever tab is active and position the underline depending on which tab is active.

Obviously with vanilla JS you could just reparent the element but I don't know how to do that in React land?

Link to comment
Share on other sites

Perhaps I'm not understanding flip correctly, but why would it matter that it is removed from the DOM? I was assuming that Flip.getState() saved all the information required to do the animation. That way, when the new element is added, it is added with a FLIP from the previous element state.

The example attached in this post:

Has more or less the same effect (conditional rendering) but is successful.

 

In this case I theoretically could move the tab up a level, my end goal is to use FLIP animations for page transitions in my react app where having the animated element up one level would not be possible.

Link to comment
Share on other sites

I'm running out the door so I don't have much time to dig into this, but I see a few problems:

  1. You are calling Flip.getState() on the element immediately after you start an animation and by default Flip will force any in-progress Flip animations of those elements (the ones you're getting state on) to completion so that when you get the state, it isn't tainted by the in-progress animation. In other words, you want the FINAL state, not the partially-done animating one. See the issue? You've got two options: get the state BEFORE you start animating from the old state (in my demo below) -OR- set {kill: false} in the config object, but be very careful about that because as I explained above, that means the state may be tainted. 
  2. As Cassie explained, you're trying to flip is an entirely new element (React re-rendered one). So you need to define a "targets" property on the Flip.from() to tell it to use that new one. You've done a good job of setting a data-flip-id attribute, so it'll match up fine with the previous state. 

https://codesandbox.io/s/romantic-wood-x7794o?file=/src/App.js

 

You'll notice some shifting in the flex divs because the underline is animating from a different width, which obviously makes the Flexbox a different size as well. There are various strategies for combating that which I can discuss later if you need help. 

  • Like 1
Link to comment
Share on other sites

@GreenSock Dang, I was close. I think I understand now. At one point I was putting the setFlip in the on complete to try and avoid issues with interrupting animations but saving it a variable is much easier.

It seems like on your example that setting `absolute` to true fixes the horizontal shifting of the flexbox, but there is some residual vertical shifting I can't account for. Perhaps more complicated, but less important, quickly clicking between tabs creates jittery movement, as I suppose is expected. I'm curious if there is a way to deal with that.

  • Like 1
Link to comment
Share on other sites

  • Solution
10 hours ago, SteveS said:

Perhaps more complicated, but less important, quickly clicking between tabs creates jittery movement, as I suppose is expected. I'm curious if there is a way to deal with that.

I'm not sure what you mean by "jittery" (I'm not seeing anything like that), but maybe you're talking about how when you click around quickly (don't let the animations finish), it jumps to where it would have started after it finished the animation, and then animates to the correct position from there(?)

 

That's because you're calling things out of order. Here's how it's supposed to happen:

  1. Get the current state with Flip.getState(). This may be mid-animation when clicking around quickly.
  2. Change state (do whatever you want, shift the DOM around, restyle things, whatever)
  3. Flip.from()

But here's how you are doing it in your demo: 

  1. Change the state
  2. Record the new/changed state using Flip.getState()
  3. Now use a PREVIOUS/OLD version of #2 and animate to the current state from that old one. 

See the problem? What if you click around quickly? Where is the spot where you'd capture the mid-transition state? There isn't one - your code is assuming things will always complete their transition. That's why you see things jumping to start their animation from the completed state transition spot. It's because that's how you're recording it.

 

Here's an updated demo that puts things more in the proper order by grabbing the state BEFORE you call setCurrentTab() (in the onClick):

https://codesandbox.io/s/romantic-wood-x7794o?file=/src/App.js

 

Now that'll dynamically grab the position/size data at the right spot so that it works even when you do it mid-transition. 

 

10 hours ago, SteveS said:

It seems like on your example that setting `absolute` to true fixes the horizontal shifting of the flexbox, but there is some residual vertical shifting I can't account for.

That's because your flipping element is 2px tall and it's propping open its container, so when you set it to position: absolute for the transition, it no longer props open its container. There are many ways you could address that, like by setting an explicit height or putting an invisible 2px tall <div> elsewhere or using CSS rules such that the non-active tabs take up 2px more height, etc. Or sometimes it makes sense to include all the elements in that grouping in the Flip animation so that they're all set to position: absolute. 

 

Does that help clear things up? 

 

If you're deep in React land with complex FLIP stuff going on, you MIGHT benefit from Flip.batch() which we specifically engineered to solve some tricky React scenarios with nested components and funky timings. It's more of a low-level thing, but you seem like a pretty advanced guy who could appreciate knowing about it. 

  • Thanks 1
Link to comment
Share on other sites

These examples have been very helpful. I'll have to do some more messing around with this to really get a feel for how it is supposed to fit in with everything else. The example on the Flip.batch() documentation is pretty crazy, but those are pretty much exactly the kind of interactions I'm trying to achieve.

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