Search code examples
reactjsanimationstateframer-motion

Framer-Motion, toggle between 2 different enter and Exit states


I'm trying to animate some elements based on weather the user is progressing forward or backward. e.g. state > prevState or state < prevState

Here is what the logic looks like.

-Hook to update state and track previous state

import { useState } from "react";
export default function useStateWithPrevious(initial) {
    const [value, setValue] = useState(initial);
    const [prev, setPrev] = useState(initial);

    function setValueAndPrev(newValue) {
        if (newValue === value) return; // optional, depends on the logic you want.
        setPrev(value);
        setValue(newValue);
    }

    return [prev, value, setValueAndPrev];
}

Hook use

const [previousRegistrationDay, registrationDay, setRegistrationDay] = useSetDayWithPreviousDay(1);

Here is the Framer Motion Component.

<AnimatePresence mode="wait">
    <motion.nav
        key={registrationDay}
        animate={{ x: "0%", opacity: 1 }}
        transition={{ duration: 0.5 }}
        initial={{ opacity: 0, x: previousRegistrationDay < registrationDay ? "100%" : "-100%" }}
        exit={{ opacity: 0, x: previousRegistrationDay < registrationDay ? "-100%" : "100%" }}
    >
        <div>
            registrationDay{registrationDay}
            <br />
            previousRegistrationDay{previousRegistrationDay}
        </div>
    </motion.nav>
</AnimatePresence>

Example https://youtu.be/lmob3oJUgkA

This works for the most part, but the first time you "go back" / "change directions" the animation exit is wrong.

It's like there is a closure around the variables defining the exit transition, causing it to be wrong the first time the prevState > state and then switching to prevState < state

Any help is greatly appreciated!


Solution

  • What you need is variants with custom argument and custom prop of AnimatePresence.

    You define your animations as variant which accepts custom argument (direction in your case) and depending on it returns desired properties. Then you need to pass direction as custom prop to both your component (to make appear animation work correctly) and to AnimatePresence to make exit animation aware of direction too.

    Here is example how it's implemented in my project:

    1. Calculate direction
    2. Define variants
    3. Pass variants and custom to component and to AnimatePresence

    UPD: I wrote an article about this with examples in my blog.