Reputation: 6987
I have essentially the following setup:
<AnimatePresence initial={false}>
{value ? (
<motion.div>
{/* Page 1 content */}
</motion.div>
) : (
<motion.div>
{/* Page 2 content */}
</motion.div>
)}
</AnimatePresence>
When value
changes from true
to false
, I want page 1 to slide out to the left while at the same time page 2 slides in from the right. This is very similar to how slideshows work, or page transitions on iOS apps.
I have a simple example set up on CodeSanbox: https://codesandbox.io/s/laughing-leftpad-4kin7k?file=/src/App.js:272-1064. What happens when I toggle value
is that the pages slide to the left as expected, but because the pages are different heights, they cause the content under them to jump down. Also, since both pages are rendered at the same time during the animation, the first page causes the 2nd page to render below it as well, so when the animation completes, page 2 jumps up.
How can I do this sort of animation without the content jumping around? Ideally, I want:
Upvotes: 3
Views: 15552
Reputation: 899
In case you are working with layoutId
you can simply append the current pathname to the id.
const pathname = usePathname();
return (
<motion.div
layoutId={`${pathname}-some-id`}
transition={{ ease: "circOut", duration: 0.5 }}
/>
)
Upvotes: 2
Reputation: 53
For newer versions of framer, mode="popLayout"
can also be used on AnimatePresence. This will not delay animations of <motion.div>
children as in mode="wait"
.
<AnimatePresence mode="popLayout">
<motion.div>....</motion.div>
</AnimatePresence>
https://www.framer.com/motion/animate-presence/###mode
Upvotes: 1
Reputation: 37
I stumbled upon this problem too. To make it so that there is no delay, and you don't have to wait until the element leaves like the other answers have, you simply have to position the elements on top of each other.
I've found the most elegant solution is to simply add a position: absolute
css style to your motion.div component. You can then wrap the outer AnimatePresence component with a div that has a position: relative
style and a set height so that it looks correct.
The structure should be like so:
<div style={{position: "relative"}}>
<AnimatePresence>
<motion.div style={{position: "absolute"}}>...</motion.div>
</AnimatePresence>
</div>
here is your modified codesandbox: https://codesandbox.io/s/hopeful-lumiere-7vwb8n?file=/src/App.js
Upvotes: 1
Reputation: 89
add mode="wait"
in your AnimationPresence component
<AnimatePresence mode="wait" initial={false}>
<motion.div>....</motion.div>
</AnimatePresence>
Upvotes: 6
Reputation: 275
You need to define exitBeforeEnter
property to the AnimatePresence
component, which should fix your issue!
Something like this :
<AnimatePresence exitBeforeEnter initial={false}>
<motion.div>....</motion.div>
</AnimatePresence>
Upvotes: 0
Reputation: 9
Unfortunately I'm running out of time to dig into finding a technical answer, but I used this Framer-Motion Example as a guide.
Steps:
Here's the sandbox I used: https://codesandbox.io/s/crazy-forest-5m1p7x?file=/src/App.js
Upvotes: 0