React-spring: Animate mount/unmount?

Created on 11 Apr 2018  路  15Comments  路  Source: pmndrs/react-spring

I'd like to animate a single element that mounts/unmounts, what's the suggested way to handle this case?

I tried with Transition but it seems to like only 2 or more elements.

Thanks.

Most helpful comment

@FezVrasta

Wait, what am i saying, this is wrong. More like this:

{({ rotation, scale, ...rest }) =>
    <div style={{ ...rest, transform: `rotation(${rotation}) scale(${scale})` } }} />}

馃槃

All 15 comments

This should work:

const MyComponent = styles => <div style={styles}>hello</div>

      <Transition
          from={{ opacity: 0 }}
          enter={{ opacity: 1 }}
          leave={{ opacity: 0 }}>
          {condition ? MyComponent : () => null}
      </Transition>

Yes that's what I ended up using as well, but I thought there was a better way...
Thanks anyway!

Well, perhaps you're right. I'll implement a fallback when children are empty.

Is it expected that with your example and these settings only the opacity gets animated?

          <Transition
            from={{ opacity: 0, rotation: '180deg', scale: 0.5 }}
            enter={{ opacity: 1, rotation: '0deg', scale: 1 }}
            leave={{ opacity: 0, rotation: '180deg', scale: 0.5 }}
          >

@FezVrasta in 4.0.4 you can do:

{condition && MyComponent}

All of them get animated, but rotation and scale are transforms, not css attributes, you can't spread them into styles just like that, you'd have to do something like this:

{({ rotation, scale, ...rest }) =>
    <div style={{ ...rest, transform: { rotation, scale } }} />}

To understand it better, think of the stuff you dump into from/to/enter/leave/update not as "styles" but tweening values. Later when you render your view you'll put them into styles, attributes, react-component-props or do whatever you like with them.

Thanks!

@FezVrasta

Wait, what am i saying, this is wrong. More like this:

{({ rotation, scale, ...rest }) =>
    <div style={{ ...rest, transform: `rotation(${rotation}) scale(${scale})` } }} />}

馃槃

@drcmda Thanks for answering this. Would you be open to having this in the example to make it clear that these need to be handled differently? Currently it seems like the docs show them passed in but not being used. Had to find this issue to make sense of how they are handled. Cheers.

edit: Actually in the end I needed to use interpolation to get scale to work at all:

<Spring
      native
      from={{ opacity: 0, color: "goldenrod", scale: 1 }}
      to={{ opacity: 1, color: "red", scale: 5 }}
    >
      {({ scale, ...rest }) => {
        return (
          <animated.div
            style={{
              ...rest,
              transform: interpolate([scale], s => `scale(${s})`)
            }}
          >
            i will fade in
          </animated.div>
        );
      }}
    </Spring>

Where interpolate() is imported from react-spring.

Scale working with interpolate
Scale not working without interpolate

I guess given your response I expected the second to work.

edit 2: It can be done. Here is scale without interpolation but working thanks to @drcmda's explanation.

Updated my answer because even @drcmda's example didn't seem to work for me so I ended up going with interpolate to make scale work.

@jimthedev interpolate is good when you need multiple values. Single values inherit interpolate() :

transform: scale.interpolate(s => `scale(${s})`)

Or ...

from={{ transform: 'scale(1)' }}
to={{ transform: 'scale(2)' }}>
  {styles => <animated.div styles={styles} ... />}

Generally it helps to think of from/to as objects that map generic values, these aren't styles or props. They become styles when you drop them into style={props} or props when you pass them as props radius={radius}

PS. the example i gave before was for non-native springs. If you use native the values you pass aren't actual values but classes.

More on native rendering here: https://github.com/drcmda/react-spring/blob/master/API-OVERVIEW.md#native-rendering-and-interpolation-demo

Oh thank you that makes more sense.

@drcmda Hi. I'm in the same situation with the OP (animate a single element that mounts/unmounts). I tried your solution as well as others (handle not condition as () => null). However, it gives me this error:

screen shot 2018-07-11 at 11 19 49 pm

This is my code:
screen shot 2018-07-11 at 11 20 28 pm

Is there something wrong with my code? Thanks!

styles => MyComponent isn't React, you probably mean

{message && MyComponent}

All transition wants is either a function or an array of functions, you're trying to give it a function that returns a reference of a function that returns a component. :-)

How would I go about doing this using the new hooks api @drcmda?

How would I go about doing this using the new hooks api @drcmda?

It works and allows to force reveal/hide the content of specific height.

import React, { FunctionComponent } from 'react';
import { animated, useSpring } from 'react-spring';

import styles  from './Animations.scss';

export interface IRevealAnimationProps {
  height: number | string;
  initialOffset?: number | string;
  show?: boolean;
}

type SpringProps = { opacity: number; x: string | number; height:  string | number; }

export const RevealAnimation: FunctionComponent<IRevealAnimationProps> = ({
  height: elementHeight,
  initialOffset = 20,
  show = true,
  children
}) => {
  const { x, height, ...rest } = useSpring<SpringProps>({
    from: { opacity: 0, x: initialOffset, height: 0 },
    opacity: show ? 1 : 0,
    x: show ? 0 : initialOffset,
    height: show ? elementHeight : 0
  });
  return (
    <animated.div className={styles['reveal-animation']} style={{ ...rest, transform: x.interpolate(x => `translateY(${x}px)`) }}>
      <animated.div style={{ height }}>{children}</animated.div>
    </animated.div>
  );
};
.reveal-animation {
  will-change: transform, opacity;
  overflow: hidden;

  > div {
    display: block;
    overflow: hidden;
  }
}
Was this page helpful?
0 / 5 - 0 ratings