pmndrs / react-spring

✌️ A spring physics based React animation library
http://www.react-spring.dev/
MIT License
28.22k stars 1.19k forks source link

[feat]: Animate Presence component addition #1955

Open joshuaellis opened 2 years ago

joshuaellis commented 2 years ago

A clear and concise description of what the feature is

A new component / hook that animates elements depending on whether their children are being rendered or not without the need to be passing data (this is similar but separate to useTransition which could be depreciated in favour of this new hook)

Why should this feature be included?

framer-motion has a component called animate presence which is fairly similar to our useTransition except with the latter, you have to pass state to the hook to control the appearance of the animated components.

This isn't the worst thing when you're only controlling one modal for instance, but if you want to control multiple it would be easier to be able to handle the animations depending on whether the dom element was mounted or not (see here for more context).

This feature would hopefully lead into #1628 which is also something I would be very interested in adding to react-spring.

Please provide an example for how this would work

Unlike framer-motion, rs has two parts, the hook that runs outside the react-render world & the animated component that interpolates those SpringValues to be applied to the DOM element.

We could create a new component in a similar API style to AnimatePresence, but it would be more inline with the direction of react-spring if we could make a hook & the an additional HOC built on animated to register these components for the hook to manipulate & track. So an ideal approach can be seen below:

const MyComponent = ({show}) => {
    const springs = useAnimatePresence({
        from: {
            opacity: 0,
        },
        enter: {
            opacity: 1
        },
        leave: {
            opacity: 0
        }
    }) 

    return show ? <animatedPresence.div style={springs}>Hello world</animatedPresence.div> : null
}

Of course, happy to hear other people's API design ideas // thoughts or even concerns about this feature.

leo-petrucci commented 2 years ago

I really like that API, it feels distinctly react-spring.

In this hypothetical situation, would the springs object be passed to an animated.div? Would useAnimatePresence and animatedPresence.div exist in the same component or could animatedPresence.div wrap a component using the hook?

joshuaellis commented 2 years ago

In this hypothetical situation, would the springs object be passed to an animated.div

Yes sorry, i've updated it now!

Would useAnimatePresence and animatedPresence.div exist in the same component or could animatedPresence.div wrap a component using the hook

Are you able to give an example of what you're suggesting?

leo-petrucci commented 2 years ago

Are you able to give an example of what you're suggesting?

My components won't always know they're being unmounted, ideally they should just know that when they are being mounted/unmounted certain animations should be played.

If animatedPresence.div is inside the component that is being unmounted, then it won't be aware that it's being unmounted and the animation won't play.

const Wrapper = ({show}) => (
  <>
  {
    show ? <MyComponent /> : null
  }
  </>
)

const MyComponent = () => {
    const springs = useAnimatePresence({
        from: {
            opacity: 0,
        },
        enter: {
            opacity: 1
        },
        leave: {
            opacity: 0
        }
    }) 

    return <animatedPresence.div style={springs}>Hello world</animatedPresence.div>
}

So I think something like this would make more sense?

const Wrapper = ({show}) => (
  // magic, knows when its children are mounted and unmounted
  <animatedPresence.div>
  {
    show ? <MyComponent /> : null
  }
  </animatedPresence.div>
)

const MyComponent = () => {
  // Will get information from `<animatedPresence.div>` above
    const springs = useAnimatePresence({
        from: {
            opacity: 0,
        },
        enter: {
            opacity: 1
        },
        leave: {
            opacity: 0
        }
    }) 

  // animations are applied to a standard `animated.div`
    return <animated.div style={springs}>Hello world</animated.div>
}
joshuaellis commented 2 years ago

Aha, I understand. Then maybe all animated components try to register themselves with the closest animatedprescence component!

And animtedpresence just wraps them 👌🏼

evangeline commented 2 years ago

Excited about this proposal! Animations not playing because of components not knowing they're unmounting is the biggest pain point when using react-spring.

This feature would also work really well with floating-ui's APIs and make the components easier to compose. Example with framer-motion's AnimatePresence here.

marioparaschiv commented 8 months ago

Any news on this API?