Framer motion and exit property not working with InertiaJs - reactjs

I would like to know how can I use "exit" property with framer motion and InertiaJs / ReactJs please.
Currently I have two pages :
profil.js
const { pathname } = window.location
...
<div>
<motion.div className="flex flex-col" layoutId={`title-3`}
initial={{opacity: 0}}
animate={{
y: [-20, 0],
opacity: [0, 1]
}}
exit={{scale: 2}}
key={{pathname}}
transition={{duration: 0.2, ease: "easeOut", delay: 0.1}}
>
<span>Profil</span>
</motion.div>
</div>
home.js
const { pathname } = window.location
...
<div>
<motion.div className="flex flex-col" layoutId={`title-3`}
initial={{opacity: 0}}
animate={{
y: [-20, 0],
opacity: [0, 1]
}}
exit={{scale: 2}}
key={{pathname}}
transition={{duration: 0.2, ease: "easeOut", delay: 0.1}}
>
<span>Home</span>
</motion.div>
</div>
And I set AnimateSharedLayout on my App (app.js) :
import { AnimateSharedLayout } from 'framer-motion';
return render(
<Provider store={store}>
<AnimateSharedLayout>
<App {...props} />
</AnimateSharedLayout>
</Provider>, el
);
When I click on "home" or "profil" in the sidebar, I see the animate, but I don't see the exit animation.
I use InertiaJs and ReactJs.
Do you have an idea why I don't see the exit ?

Once you click away from "home" or "profile", that element is removed from the DOM right away, so it won't be there long enough to see the animation.
Framer Motion Docs
By wrapping motion components with AnimatePresence, they gain the use of an exit property that can define either a set of values or a variant name to animate to before being removed.
Try wrapping your <motion.div>s with <AnimatePresence> like this:
const { pathname } = window.location
...
<div>
<AnimatePresence>
<motion.div className="flex flex-col" layoutId={`title-3`}
initial={{opacity: 0}}
animate={{
y: [-20, 0],
opacity: [0, 1]
}}
exit={{scale: 2}}
key={{pathname}}
transition={{duration: 0.2, ease: "easeOut", delay: 0.1}}
>
<span>Home</span>
</motion.div>
</AnimatePresence>
</div>

Related

framer motion exit animation not working (using react and animate presence)

it doesn't work. why?
goal: fire animation before component unmounts
example component:
import React from "react";
import { useState } from "react";
import { AnimatePresence, motion } from "framer-motion";
export default function NotExit() {
const [show, toggle] = useState(true);
const MyComponent = ({ isVisible }) => (
<AnimatePresence>
{isVisible && (
<motion.div
key="modal"
initial={{ opacity: 0 }}
animate={{ opacity: 1 }}
exit={{ opacity: 0 }}
/>
)}
</AnimatePresence>
)
return (
<div className="relative w-screen h-screen flex flex-col-reverse justify-center items-center">
<button
className="bg-purple-600 p-2 rounded"
onClick={() => {
toggle(!show);
}}
>
show
</button>
{show && (
<AnimatePresence>
<motion.section
initial={{ y: 100 }}
animate={{ y: 0 }}
exit={{ opacity: 1, transition: { duration: 1 } }}
key="cock"
className="w-44 h-44 bg-red-500 rounded-xl"
transition={{ duration: 1 }}
></motion.section>
</AnimatePresence>
)}
</div>
);
solutions tried:
adding key prop,
adding duration to exit prop if for some reason the animation happens but quickly so i don't notice it
adding mode="wait" to AnimatePresence
I haven't really tried the switch location thingy mainly because i couldn't import it and i think routes replaced it
The placement of the AnimatePresence component here is not correct:
{show && (
<AnimatePresence>
<motion.section
...
></motion.section>
</AnimatePresence>
)}
You need to move it outside to wrap the animated component:
<AnimatePresence>
{show && (
<motion.section
...
></motion.section>
)}
</AnimatePresence>

Animate background using Framer Motion

I'm trying to create a React component where when scrolling down into it, its background transitions from a color to an image.
This is the component:
const TeamSection = () => {
return (
<Background>
<TeamSectionContainer
initial={{
opacity: 0,
}}
whileInView={{
opacity: 1,
}}
transition={{
ease: "easeOut",
duration: 2,
}}
>
<Title>Text</Title>
<Team>Text<Team>
</TeamSectionContainer>
</Background>
);
};
And the styles:
const TeamSectionContainer = styled(motion.div)`
${tw`
bg-no-repeat
w-full
min-h-[200vh]
flex
flex-col
`};
background-image: url(${BackgroundImage});
`;
const Background = tw.div`
bg-[#001F33]
`;
The result is that th animation works for the whole TeamSectionContainer component, indluding its background. I'm trying to make it so that none of the content is animated and only the background is.
How could be this achieved?
I don't think you can animate the opacity of a background image like that. You'd need to add a separate div inside the TeamSectionContainer with the background image and apply the animation to that:
const TeamSection = () => {
return (
<Background>
<TeamSectionContainer>
<motion.div className="backgroundImage"
initial={{
opacity: 0,
}}
whileInView={{
opacity: 1,
}}
transition={{
ease: "easeOut",
duration: 2,
}}
/>
<Title>Text</Title>
<Team>Text<Team>
</TeamSectionContainer>
</Background>
);
};

AnimatePresence show both elements when animating

I am to make a step-like animation, hence, when I click a button, another div than the previous is shown, e.g:
import { motion, AnimatePresence } from 'framer-motion'
const MyApp= props => {
const [count, setCount] = useState(0)
return (
<>
<AnimatePresence>
{count == 0 && (
<motion.div
transition={{ duration: 2 }}
initial={{ opacity: 1 }}
animate={{ opacity: 1 }}
exit={{ opacity: 0 }}
>
Hello 1
<button onClick={() => { setCount(count + 1) }}
</motion.div>
)}
</AnimatePresence>
<AnimatePresence>
{count == 1 && (
<motion.div
transition={{ duration: 2 }}
initial={{ opacity: 1 }}
animate={{ opacity: 1 }}
exit={{ opacity: 0 }}
>
Hello 2
<button onClick={() => { setCount(count + 1) }}
</motion.div>
)}
</AnimatePresence>
</>
)
}
export default MyApp
However, the issue is, that when one div is fading out/in, the other is also fading in/out, hence, I have two elements at the same time. Instead I thought it would just animate one, remove it, and animate the other.
Am I doing something wrong here ?
Since you want to animate them together they should be under one <AnimatePresence />
AnimatePresence requires you to add an explicit key on the component you are animating beneath it so that it can track the components as they are added and removed https://www.framer.com/api/motion/animate-presence/#unmount-animations
Since you want to replace one component with another you need to add the prop exitBeforeEnter on the AnimatePrensence. https://www.framer.com/api/motion/animate-presence/#animatepresenceprops.exitbeforeenter
<AnimatePresence exitBeforeEnter> // Note on #3
{isGreenBox ? (// Note on #1
<GreenBox
initial={{ scale: 0 }}
animate={{ scale: 1 }}
exit={{ scale: 0 }}
key="green" // Note on #2
/>
) : (
<PurpleBox
initial={{ scale: 0 }}
animate={{ scale: 1 }}
exit={{ scale: 0 }}
key="purple" // Note on #2
/>
)}
</AnimatePresence>
Here is a working example I created :) https://codesandbox.io/s/framer-motion-animation-presence-exitbeforeenter-0miq0

How can i switch between react components using framer motion?

In my react app i need to switch between components like in a carousel. I found this example to build an image carousel only using framer motion: https://codesandbox.io/s/framer-motion-image-gallery-pqvx3?file=/src/Example.tsx:1715-1725
I want to adapt this to switching between components. At the moment my page looks something like this:
const variants = {
enter: (direction: number) => {
return {
x: direction > 0 ? 100 : -100,
opacity: 0,
}
},
center: {
zIndex: 1,
x: 0,
opacity: 1,
},
exit: (direction: number) => {
return {
zIndex: 0,
x: direction < 0 ? 100 : -100,
opacity: 0,
}
},
}
const Page = () => {
const [[page, direction], setPage] = useState([0, 0])
const paginate = (newDirection: number) => {
setPage([page + newDirection, newDirection])
}
return (
<motion.div
key={page}
custom={direction}
variants={variants}
initial="enter"
animate="center"
exit="exit"
>
<!-- my components, between which I want to switch, should appear here -->
</motion.div>
)
}
how would I have to build the logic to be able to switch dynamic between my components (slides)? In the codesandbox example the images were changed via an array:
const imageIndex = wrap(0, images.length, page);
<motion.img key={page} src={images[imageIndex]} />
How could i do that to switch between jsx elements?
Edit
The answer from Joshua Wootonn is correct, but you need to add the custom prop also to the TestComp to get the animation working with dynamic variants like this:
const TestComp = ({ bg }: { bg: string }) => (
<motion.div
custom={direction}
variants={variants}
initial="enter"
animate="center"
exit="exit"
transition={{
x: { type: "spring", stiffness: 100, damping: 30 },
opacity: { duration: 0.2 },
}}
className="absolute w-full h-full"
style={{
background: bg,
}}
/>
)
A couple of things were missing from the above answer to get exit animations working.
If you want exit animations to work within AnimationPresense you need to set keys on its children
<AnimatePresence initial={false} custom={direction}>
{page === 0 && <TestComp key="0" bg="rgb(171, 135, 255)" />}
{page === 1 && <TestComp key="1" bg="rgb(68, 109, 246)" />}
{page === 2 && <TestComp key="2" bg="rgb(172, 236, 161)" />}
</AnimatePresence>
If you want to animate something in while something is still animating out without having massive content shifting, you need to take them out of the flow. (use absolute positioning and wrap with relatively positioned container)
<div style={{ position: "relative", height: "300px", width: "300px" }}>
<AnimatePresence initial={false} custom={direction}>
...
</AnimatePresence>
</div>
and on the child components
height: 100%;
width: 100%;
position: absolute;
Working codesandbox: https://codesandbox.io/s/framer-motion-carousel-animation-wetrf?file=/src/App.tsx:658-708
Your components should return <motion.div> (or <motion.section>, <motion.span> etc.).
And in the page component you should use <AnimatePresence /> component (like in the example):
<AnimatePresence initial={false} custom={direction}>
{COMPONENTS}
</AnimatePresence>
Then you have to decide which component will appear:
{page === 0 && <ComponentOne />}
{page === 1 && <ComponentTwo/>}
{page === 2 && <ComponentThree/>}
The animations you can control with variants.
You can see a quick demo here: https://codesandbox.io/s/quizzical-hypatia-7wqjc?file=/src/App.tsx

Exit Animations problem in Route Transitions with framer-motion

I have been trying to animate route transitions using framer-motion but can't wrap my head around the exit animations. Entry animations work as they should.
I want the component to slide in from the left and slide to the right when route changes.
I havewrapped the Switch component using AnimatedPresence from framer-motion and used a location object and taken its pathname as its key.
Wrapped all of this using BrowserRouter
Ensured I have used motion component from framer-motion at the root element of each of my functional components
Codesandbox
App.js
function App() {
const location = useLocation();
return (
<div className="App">
<AnimateTrans loc={location} />
</div>
);
}
AnimateTrans.jsx
export default function (props) {
return (
<AnimatePresence exitBeforeEnter>
<Switch location={props.loc} key={props.loc.pathname}>
<Route exact path="/">
<First />
</Route>
<Route exact path="/sec">
<Second />
</Route>
</Switch>
</AnimatePresence>
);
}
First.jsx
const transition = {
duration: 1,
type: "spring"
};
const transVariants = {
init: { scale: 0.3, opacity: 1, x: -400, transition: { transition } },
anim: { scale: 1, opacity: 1, x: 0, transition: { transition } },
exit: { scale: 0.4, opacity: 0, x: 400, transition: { transition } }
};
return (
<motion.div
variants={transVariants}
initial="init"
animate="anim"
exit="exit"
className="container"
>
<div className="content">
<img src="https://encrypted-tbn0.gstatic.com/images?q=tbn:ANd9GcQ1a79XgKvvi2VyAu54LI21MbKrfSlZTL9iPA&usqp=CAU" />
</div>
<div className="oth"></div>
d
</motion.div>
);
Second.jsx
const transition = {
duration: 1,
type: "spring"
};
const transVariants = {
initial: { scale: 0.3, opacity: 1, x: -400, transition: { transition } },
animate: { scale: 1, opacity: 1, x: 0, transition: { transition } },
exit1: { scale: 0.4, opacity: 0, x: 400, transition: { transition } }
};
return (
<motion.div
variants={transVariants}
initial="initial"
animate="animate"
exit="exit1"
className="container"
>
<div className="content">
<img src="https://fashioneditorials.com/wp-content/uploads/2017/10/i-D-Japan-Chanel-Haute-Couture-With-Nana-Komatsu-Angelo-Pennetta-1.jpg" />
</div>
<div className="oth"></div>
d
</motion.div>
);
}
Is my location object at fault here? Should I change the key or structure App.js differently?
You are using the <a> tag to route, you need to use Link from react-router-dom. Then It's gonna work.

Resources