Search code examples
reactjsanimationreduxtailwind-cssframer-motion

exit animation for framer motion not working


below is a code for a stepper that displays certain text in each step , the number of the steps is highlighted (by changing the background color of the step number) , and it will display a text beneath it , my issue is i wanted to use framer-motion with AnimatePresense and exit prop for the transitioning from one text to another but its not wokring , i would appreciate the feed back below is the animation variants export const instructionVariant = { hidden: { opacity: 0, x: "100vw", }, visible: { opacity: 1, x: 0, transition: { type: "spring", mass: 0.7, damping: 7, ease: "easeIn", }, }, exit: { x: "-100vw", opacity: 0, transition: { duration: 0.5, ease: "easeInOut" }, }, };

export const paragraphVariant = {
  hidden: {
    opacity: 0,
    x: "10hw",
  },
  visible: {
    opacity: 1,
    x: 0,
    transition: {
      type: "spring",
      mass: 0.7,
      damping: 7,
      ease: "easeIn",
    },
  },
  exit: {
    x: "-5hw",
    opacity: 0,
    transition: { duration: 0.5, ease: "easeInOut" },
  },
};

below is the code , i used redux-toolkit for this , if i need to ppost the code for it if needed

const Instructions: FC = () => {
  const step = useAppSelector((state) => state.instruction.step);
  const dispatch = useAppDispatch();

  return (
    <motion.div
      variants={instructionVariant}
      initial="hidden"
      animate="visible"
      exit="exit"
      className="h-2/4 w-3/5 bg-primary-200 border-2 border-solid border-black rounded-lg shadow-lg shadow-black relative"
    >
      <div className="grid grid-rows-[1fr_4fr]">
        <div className="flex justify-around items-center m-10">
          {data.map((item: InstructionsProps) => {
            return (
              <>
                <div>
                  <h1
                    className={
                      step >= item.id
                        ? "flex justify-center items-center text-4xl rounded-full bg-secondary-100 text-white w-16 h-16 transition-all duration-300 ease-in"
                        : "flex justify-center items-center text-4xl rounded-full bg-primary-100 text-white w-16 h-16 transition-all duration-300 ease-in"
                    }
                  >
                    {item.id - 1}
                  </h1>
                </div>
                <div
                  className={
                    step > item.id
                      ? "last:hidden text-secondary-100 transition-all duration-300 ease-in"
                      : "last:hidden transition-all duration-300 ease-in"
                  }
                >
                  <FaArrowRight size={20} />
                </div>
              </>
            );
          })}
        </div>
        <AnimatePresence exitBeforeEnter>
          <motion.h1
            variants={paragraphVariant}
            initial="hidden"
            animate="visible"
            exit="exit"
            className="m-10 text-xl font-bold font-rubik"
          >
            {data[step - 1].description}
          </motion.h1>
        </AnimatePresence>
      </div>
      <div className="absolute right-6 bottom-6 flex justify-between items-center w-32">
        <button
          onClick={() => dispatch(instructionsAction.instructionreverse())}
          className="hover:scale-125 active:scale-110 active:text-secondary-200 hover:text-secondary-200 transition-all duration-300 ease-in"
        >
          <HiOutlineChevronDoubleLeft size={35} />
        </button>
        <button
          onClick={() => dispatch(instructionsAction.instructioncomplt())}
          className="hover:scale-125 active:scale-110 active:text-secondary-200 hover:text-secondary-200 transition-all duration-300 ease-in"
        >
          <HiOutlineChevronDoubleRight size={35} />
        </button>
      </div>
    </motion.div>
  );
};

Solution

  • From the AnimatePresence docs:

    Direct children must each have a unique key prop so AnimatePresence can track their presence in the tree.

    const MyComponent = ({ isVisible }) => (
      <AnimatePresence>
        {isVisible && (
          <motion.div
            key="modal"
            initial={{ opacity: 0 }}
            animate={{ opacity: 1 }}
            exit={{ opacity: 0 }}
          />
        )}
      </AnimatePresence>
    )