framer / motion

Open source, production-ready animation and gesture library for React
https://framer.com/motion
MIT License
22.81k stars 762 forks source link

[FEATURE] Option to conditionally disable layoutId animations #2075

Open Sebastian-Nielsen opened 1 year ago

Sebastian-Nielsen commented 1 year ago

Is your feature request related to a problem? Please describe. I'm always frustrated when needing to conditionally disable layoutId animations for a short period of time, because it is not currently possible.

Describe the solution you'd like I would like to be able to pass an attribute that determines whether or not the layoutId animation is disabled dynamically.

<motion.div layoutId={'id'} isLayoutDisabled={}/>

Wesley-sudo commented 1 year ago

Yes, I need this too. What I was going to recommend was be able to do <motion.div layoutId={toggle ? 'someId' : undefined/null}/>. But, currently this doesn't work - it's treating the undefined or null to be a unique ID.

jrolfs commented 7 months ago

So I really needed something like this (or an updatable layoutId) for something I've been struggling with and off for a couple of weeks. I finally came up with a passing workaround, so I figured I should share it here.

In my case, I have a list of items that can go fullscreen, and I use layoutId to animate them there. The fullscreen view also has pagination via AnimatePresence. As the items exit the carousel, they're mounted again in the list behind the fullscreen view. The problem is that this causes the layout animation to run in addition to the exit animation.

I tried all kinds of shit to disable the layout animation for that case (layout={false}, unmounting things selectively, conditional namespaced LayoutGroup, ... and more 😥), but they all seemed to cause issues with the layout animation in Safari.

Finally, I thought to nest another motion component in the AnimatePresence child and set layoutId there. Then, I could override the opacity and transform that the layout animation applies conditionally with !important on the nested element. This effectively nullifies the layout animation when I need to. It's far from ideal (it still runs the animation and fires events), but it's working well for me.

Another technique I use sometimes, in less extreme cases, is to conditionally set the duration of the layout transition to 0.

I hope this helps someone, and I hope to be able to contribute some improvements to the layoutId stuff at some point. This is such a fantastic library. Thanks for all your hard work @mattgperry!

jrolfs commented 7 months ago

@Wesley-sudo, the problem isn't that it's undefined. It's that once a component is mounted, its layoutId can't be updated unless you remount the component. See #1411.

dominik2323 commented 3 months ago

You could do something like this: const layoutAnimationProps = disableLayoutAnimation ? {} : { layout: true, layoutId: id };

and then expand layoutAnimationProps in motion.div like this: <motion.div {...layoutAnimationProps} />

jrolfs commented 1 month ago

@dominik2323 in my experience, this condition will not effectively update the layoutId unless the component is remounted. It's possible this has changed with a newer version of motion, but https://github.com/framer/motion/issues/1411 makes me think that isn't the case.

dominik2323 commented 1 month ago

It worked well in my case, but if I remmeber correctly, I didn't changed the props after the component was mounted.