React Transition Group slide elements up

10,212

Solution 1

So, I asked this question on the Github repo for this library, and got a reply with a proper working version. Until the guy who responded posts an answer here, I want to share his answer.

Hi there, you have two issues with the codepen. The first is you aren't using a stable key for your list items, so removing something in the middle of the list won't work right. The second is that your setup is correct, and the timeout is working and the animation is playing, but you don't see the animation for height play because you can't animate from height: auto with plain css transitions.

here https://codepen.io/anon/pen/dzPvEO?editors=0110 is a working pen but it requires setting an explicit hieght on the items (max-height isn't enough). One way of dealing with this neatly in a dynamic manner is to use the onExit callback to measure and set the height on the exiting item so it has an explicit height set while exiting

So the first thing was setting a more consistent key property value:

<CSSTransition
    key={c.name}
    classNames="customer"
    timeout={{ enter: 500, exit: 700 }}
>        
    <CustomerRow
        customer={c}
        onDelete={() => onDelete(i, c)}
    />
</CSSTransition>

Secondly was to make sure I set a height on the containing div class.

Solution 2

Is this better? I used keyframes:

.customer-exit {
  opacity: 0;
  /*transition: opacity 300ms ease, height 400ms ease 300ms;*/
      -webkit-animation: slideIn 0.7s ease;forwards;
    -moz-animation: slideIn 0.7s ease;
    animation: slideIn 0.7s ease;
}

.customer-exit.customer-exit-active {
  opacity: 0.01;
  height: 0px;
}

@keyframes slideIn {
    0% {
       opacity:1
    }

      50% {
       opacity:0
    }
       90% {
       transform: translate(0,-100px);
    }
    100% {
      opacity:0
        /*transform: translateY(0px);*/
        /*opacity:1*/
    }
}

https://codepen.io/vladop/pen/PKwmMg

Solution 3

I wanted to share a recent library that solves these types of problems very easily and intuitively, called React Sequencer.

The library is similar to ReactCSSTransition only it gives you full control over the stages of sequences and their durations. I have made an example that solves your problem here:

https://codesandbox.io/s/nrw3261m20

What's nice is that the solution doesn't require any repaint hacks or clunky callbacks - but performs the animation just by giving you state and letting you render the state you like.

The example shows how you could use it to fade an element out, and then collapse it as a second step in the sequence, making for a very smooth looking animation.

Share:
10,212
Callum Linington
Author by

Callum Linington

I'm a professional software developer, C#, .NET stack and Web Development. I love rugby, play rugby, breathe rugby. Music lover, Gym lover/hater.

Updated on July 01, 2022

Comments

  • Callum Linington
    Callum Linington almost 2 years

    My current version is hiding each row, but it happens too quickly, as you can see here in my codepen. Repro by deleting the top element.

    I would prefer if you the events unfolded like this:

    1. Fade out
    2. Slide up

    I'm unsure how to do this using CSS transitions and the ReactTransitionGroup

    If I can get to the stage that you see the element disappearing, then everything bunching up that would be a great start!!

    My transition stuff:

    const CustomerList = ({ onDelete, customers }) => {
      return (
        <div class="customer-list">
          <TransitionGroup>
            {customers.map((c, i) => 
              <CSSTransition
                key={i}
                classNames="customer"
                timeout={{ enter: 500, exit: 1200 }}
              >        
                <CustomerRow
                  customer={c}
                  onDelete={() => onDelete(i, c)}
                />
              </CSSTransition>
            )}  
          </TransitionGroup>
        </div>
      );
    }
    

    My CSS:

    .customer-enter {
      opacity: 0.01;
    }
    
    .customer-enter.customer-enter-active {
      opacity: 1;
      transition: 500ms;
    }
    
    .customer-exit {
      opacity: 1;
    }
    
    .customer-exit.customer-exit-active {
      opacity: 0.01;
      transition: 1200ms;
    }
    

    Update

    I've figured out with css you can have two transitions happening in sequence something like this

    .something {
    
      width: 200px;
      height: 100px;
      background-color: black;
    
      transition: background-color 200ms ease, height 200ms ease 200ms;
    
    }
    
    .something:hover {
      height: 0px;
      background-color: blue;
    }
    

    So it is just a case of the <CSSTransition timeout={} /> actually waiting for it...

    React Update

    I have the "effect" working.... see codepen

    But, obviously here, the elements still remain, which isn't the right functional behaviour