Search code examples
reactjsreact-spring

Jerky movement in react-spring transition between two divs


I am using react-spring to create animations for "additional info" box with for an item selected by the user. Following the Simple Transition Demo from the documentation. I came up with an version for my use case:

const Display = ({ item }) => (item ? <p>Index #{item}</p> : <div />);

const AnimatedDisplay = ({ item }) => {
  const transitions = useTransition(item, i => i, {
    from: { transform: "translate3d(150%, 0,0)", opacity: 0 },
    enter: { transform: "translate3d(0%, 0,0)", opacity: 1 },
    leave: { transform: "translate3d(-150%,0, 0)", opacity: 0 }
  });

  return (
    <div className="ui">
      {transitions.map(({ item: i, key, props }) => (
        <animated.div key={key} style={props}>
          <Display item={i} />
        </animated.div>
      ))}
    </div>
  );
};

Example in CodeSandbox.io

In the example clicking Select simulates clicking on a random item and Clear simulates selecting no item. When you click Select and Clear it behaves as I intended.

The problem I am trying to solve is when you press Select twice the second item enters and moved toward the first but stops short of the final position and jerks forward at the end. From other stack overflow answers it seems that some aspect of the first div is still being rendered. My assumption is this is an issue with how I have the CSS laid out but I cannot figure out what I should be doing differently:

.ui {
  width: 100%;

  display: inline-flex;
  justify-content: center;
}

I've tried several iteration with and without the flex, but the issue is exactly the same. Can anyone tell what I am doing wrong in this example?


Solution

  • You are right the problem is, that the opacity of the first div is gradually decreases. And after it becomes 0 it will be removed. That's when the second div jerks a little, because in the flex layout the space of the first div become available. The simplest solution is that you use absolute layout. So the divs will be on top of each other. You can insert the position style to the useTrasition. For example:

    const transitions = useTransition(item, i => i, {
      from: { transform: "translate3d(150%, 0,0)", opacity: 0, position: "absolute" },
      enter: { transform: "translate3d(0%, 0,0)", opacity: 1 },
      leave: { transform: "translate3d(-150%,0, 0)", opacity: 0 }
    });