简体   繁体   中英

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,
    }}
  />
)

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

A couple of things were missing from the above answer to get exit animations working.

  1. 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>
  1. 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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM