1.24- React masterClass (Framer motion2)

hun2__2·2022년 1월 26일
0

Have a fruitful vacation

목록 보기
21/24

이번에는 슬라이더 기능과 카드 기능을 만들어 볼 것이다.

framer motion을 이용하면 슬라이더 기능이 개 쉽게 만들어진다... 놀랍다,,,,

먼저 슬라이더를 만들 때는 이전에 사용하지 않은 animation이 하나 더 추가된다.
이전에는 초기, 진행, 애니메이션 종료 이렇게만 필요했지만 이제 애니메이션 종류 후 컴포넌트가 사라질때의 애니메이션이 추가된다.
이 기능을 위해 farmaer-motion의 Aniamatepresence 모듈이 필요하다.

AnimatePresence

AnimatePresence를 부모로 가지고 있는 motion component들은 exit를 prop로 가질 수 있고 initial, animate와 같이 exit에 animation을 넣어 사용할 수 있다.
AnimatePresence를 사용할 때 한가지 규칙이 있는데 태그 안의 값으로 조건문이 들어가야 한다. visible일 경우와 아닐 경우 두가지가 모두 들어가 있어야 한다. 새로 나타나거나 사라지거나 하는 값을 animate해주는 것이다.

exit

const boxVariants = {
  initial: { x: 300, scale: 0 },
  animate: { x: 0, scale: 1 },
  exit: { x: -300, scale: 0 },
};

// eslint-disable-next-line import/no-anonymous-default-export
export default function () {
  const [visible, setVisible] = useState(1);

  const nextBtn = () => {
    setVisible((prev) => (prev === 5 ? 1 : prev + 1));
  };

  const prevBtn = () => {
    setVisible((prev) => (prev === 1 ? 5 : prev - 1));
  };
  return (
    <Wrapper>
      <AnimatePresence>
        <Box
          key={visible}
          variants={boxVariants}
          initial="initial"
          animate="animate"
          exit="exit"
        >
          {visible}
        </Box>
        <BtnList>
          <button onClick={nextBtn}>prev</button>
          <button onClick={prevBtn}>next</button>
        </BtnList>
      </AnimatePresence>
    </Wrapper>
  );
}

이렇게 Box component가 종료될때 animation을 넣어줄 수 있다.

cf) key값, re-rendering

react에서 map사용시 key값을 부여하는 이유는 형제들 사이의 각 element에 고유한 key값을 주어 고유성을 부여하기 위함이다. 하지만 useState값을 key값으로 준다면
따라서

        {[1, 2, 3, 4, 5].map((i) =>
          i === visible ? (
            <Box
              key={i}
              variants={boxVariants}
              initial="entry"
              animate="center"
              exit="exit"
            >
              {i}
            </Box>
          ) : null
        )}

위, 아래 코드는 기능적으로 동일하다

        <Box
          key={visible}
          variants={boxVariants}
          initial="entry"
          animate="center"
          exit="exit"
        >
          {visible}
        </Box>

custom : variants에 data를보낼 수 있는 property

위의 코드의 문제점은 prev button을 눌러도 next와 동일하게 오른쪽에서 왼쪽으로 animation이 작용된다
prev를 누르면 반대방향으로 만들기 위해서 boxVariants의 x축 값을 반대로 하여 같은 것을 다시 작성하는 방법도 있지만 custom을 사용하면 효율적으로 작성할 수 있다.
custom은 variants에서 변수 느낌으로 사용할 수 있다.
AnimatePresence과 사용할 component에 custom을 모두 넣어주면 된다.

const boxVariants = {
  initial: (custom: boolean) => ({
    x: custom ? -400 : 400,
    opacity: 0,
    scale: 0,
  }),
  animate: { x: 0, opacity: 1, scale: 1, transition: { duration: 0.5 } },
  exit: (custom: boolean) => ({
    x: custom ? 400 : -400,
    opacity: 0,
    scale: 0,
    transition: { duration: 0.5 },
  }),
};
...
      <AnimatePresence custom={back} initial={false}>
        <Box
          custom={back}
          key={visible}
          variants={boxVariants}
          initial="initial"
          animate="animate"
          exit="exit"
        >
          {visible}
        </Box>
        <BtnList>
          <button onClick={nextBtn}>prev</button>
          <button onClick={prevBtn}>next</button>
        </BtnList>
      </AnimatePresence>

exitBeforeEnter :exit animate 끝나고 다음 elemnet 시작

현재로직은 한 component가 사라지면서 exit animation과 다음 component의 init animation이 동시에 작동된다. 이것을 방지하기 위해 exit animation이 끝난 후 init animation이 작동하게 해주기 위해서 AnimatePresence에 exitBeforeEnter 속성을 부여하면 exit animation 종류 후 init animation이 작동한다.

const boxVariants = {
  entry: (custom: boolean) => ({
    x: custom ? -400 : 400,
    opacity: 0,
    scale: 0,
  }),
  center: { x: 0, opacity: 1, scale: 1, transition: { duration: 0.5 } },
  exit: (custom: boolean) => ({
    x: custom ? 400 : -400,
    opacity: 0,
    scale: 0,
    transition: { duration: 0.5 },
  }),
};

// eslint-disable-next-line import/no-anonymous-default-export
export default function () {
  const [visible, setVisible] = useState(1);
  const [back, setBack] = useState(false);
  const nextBtn = () => {
    setBack(false);
    setVisible((prev) => (prev === 5 ? 1 : prev + 1));
  };
  const prevBtn = () => {
    setBack(true);
    setVisible((prev) => (prev === 1 ? 5 : prev - 1));
  };

  return (
    <Wrapper>
      <AnimatePresence exitBeforeEnter custom={back} initial={false}>
        <Box
          custom={back}
          variants={boxVariants}
          initial="entry"
          animate="center"
          exit="exit"
          key={visible}
        >
          {visible}
        </Box>
      </AnimatePresence>
      <BtnList>
        <button onClick={prevBtn}>prev</button>
        <button onClick={nextBtn}>next</button>
      </BtnList>
    </Wrapper>
  );
}

cf) AnimatePresence initial={false}를 추가해주면 초기렌더링시에는 animation을 없애주고 다음 렌더링부터 적용할 수 있다.

클릭시 모달창 느낌으로 줌인 되는 layout card를 만들어 보았다.

layout animation, share layout animation

는 내일 이이서 작성해야겠다ㅎ,,,

ps.
건강이 제일이다... 몸살오니깐 아무것도 못함ㅠ

profile
과정을 적는 곳

0개의 댓글