Jump to content
GreenSock

gabriel.ortiz

Use React Transition and TransitionGroup to replace image in carousel

Recommended Posts

I'm noodling around with React/TransitionGroup/GSAP and I'm attempting to make a carousel. 

 

From an array of images, I'm setting the state to be one object. Using transition group, I want to leverage greensock to transition one image out, followed by one image in.  I'm using state to keep track of the "active" image, there is only one image in the state. So the rationale is, when this state update, the old state will transition out, and then replaced by the new node in the state. So there isn't a group of components per-se.

 

I'm hoping that I can get Transition group to transition the old image out, while transitioning the new image in.. I've gotten some stuff to work thanks to posts from @Rodrigo. But I'm definitely stuck now. Here is the codesandboxhttps://codesandbox.io/s/carousel-gsap-transitiongroup-z9pd3?file=/src/ImgCard.js

 

I feel like the issue has something to do with this:

    <Transition
      timeout={500}
      mountOnEnter
      unmountOnExit
      appear
      in={show}
      onEnter={(node) => TweenLite.set(node, startState)}
      addEndListener={(node, done) => {
        TweenLite.to(node, 1, {
          autoAlpha: show ? 1 : 0,
          y: show ? 0 : 50,
          onComplete: done
        });
      }}
    >
      <li>
        <img src={cardData.image} alt={cardData.name} />
      </li>
    </Transition>

 

 Any advice would be throughly appreciated!

Link to comment
Share on other sites

Hey Gabriel. How do I get the animation to play in that demo?

 

Also I noticed you're using the old syntax. We highly recommend the new sleeker GSAP syntax that lets you make use of new GSAP features like defaults. Upgrading is easy:

 

Link to comment
Share on other sites

Hi @ZachSaucier, Thanks for pointing out the legacy GSAP, I switched out the basics right away. And apologies about the confusing layout, it's not very good it's just for prototyping. It's not responsive at all. But on the left and right on the container there are arrows, and that triggered the image to animate. 

 

What I'm noticing is the image transition will properly play when the new image is mounted, but it won't play the transition when the image is unmounted. I basically thought that I could control the entering image using the `onEntering` property, and use `addEndListener` to control the image existing. 

 

Link to comment
Share on other sites

Hey, I've been trying to look at this all morning but never got enough time.

 

The problem is CSS and nothing else. The point is that while the transition is happening both elements are in the DOM at the same time, since there is not enough space to place them the new element moves the current element down and with the overflow set to hidden you can't see it happening. If you remove the overflow: hidden from the parent <ul>, you'll see it happening (here is the dev tools showing the previous element below the new element):

q50OTJ0.jpg

 

Basically you have to set the new element's position to absolute and when the animation is complete remove that style and it should work as expected. Of course you might have to tinker other elements' style in order to correctly show the <li> with the absolute position, but that's about it.

 

This sample uses that approach. As you can see the outer-wrapper element on each component has a position-absolute class, which is self-explanatory:

 

https://stackblitz.com/edit/gsap-react-route-animation?file=components.js

 

Happy Tweening!!!

  • Like 3
Link to comment
Share on other sites

GAFF! you are totally right, that was the the ticket. Thank you for pointing it out I was basing my what i know from the "Animating Routes" example

 

If you could spare a second @Rodrigo i was wondering if you could share insight into why we use a ternary to set the styles for the animations, ie: Does addEndListener run on both mount and unmount? 

 

<Transition
  unmountOnExit
  in={props.show}
  timeout={1000}
  onEnter={node => TweenLite.set(node, startState)}
  addEndListener={ (node, done) => {
    TweenLite.to(node, 0.5, {
      autoAlpha: props.show ? 1 : 0,
      y: props.show ? 0 : 50,
      onComplete: done
    });
  }}
>

So does it work like this:

onEnter => set styles before mount

addEndListener (props.show is true) => entering mounting animation

addEndListener (props.show is false) => existing unmounting animation 

 

Thanks for the insight!

Link to comment
Share on other sites

Hey,

 

Indeed, keep in mind that <Transition> is just a React component so, when you update a prop in it (for these samples the in prop), it re-renders. 

 

You can look into more details here:

 

https://github.com/reactjs/react-transition-group/blob/399f26998a5cf2449798a02c755e5808e9b39ddd/src/Transition.js

 

Basically the componentDidUpdate method is triggered when the in prop is updated (false -> true; true -> false) which updates the status property in the component's state (this is basically what the CSS transitions use), which ultimately triggers a series of callbacks which end up calling, among others, the addEndListener one.

 

The ternary operator basically is a shorthand for an if() {} else {} conditional block. Since the animation is basically the same (animate the Y transform value) but what changes is the value, we use that instead of re-writing all the code twice like this:

addEndListener={ (node, done) => {
  if (props.show) {
    gsap.to(node, {
      duration: 0.5,
      y: 0,
      autoAlpha: 1,
      onComplete: done
    });
  } else {
    gsap.to(node, {
      duration: 0.5,
      y: 50,
      autoAlpha: 0,
      onComplete: done
    });
  }
}}

In terms of performance there shouldn't be any difference between one approach and the other, is just less code. Now if you want to animate completely different properties of the DOM node for each animation, you can either use this approach or again use a ternary operator to create the configuration object and use it in a GSAP instance. Those are just different alternatives that javascript gives us in order to do that.

 

To summarize: basically on every re-render the function passed to addEndListener will be executed (keep in mind that we're passing an anonymous function) and you'll have access to the props the <Transition> component has (look into the github link to check the inner work of the component, which is not extremely convoluted).

 

Hopefully this makes it more clear.

 

Happy Tweening!!!

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