[React] Framer-Motion으로 슬라이드 만들기(기본편)

soyeon·2022년 3월 31일
6

TIL

목록 보기
15/31

Framer-Motion

공식 홈페이지: https://www.framer.com/motion/
리액트용 모션 라이브러리입니다.
이것저것 세련된 애니메이션 효과들을 쉽게 사용할 수 있습니다.

설치

npm install framer-motion

Import

import { motion } from "framer-motion"

기본 사용방법

motion이 들어갈 요소를 지정하기

HTML, SVG 요소에 motion을 붙여줍니다.

<motion.div animate={{ scale: 0.5 }} />

styled-component를 사용하는 경우에는 styled(motion.html요소)로 작성합니다.

  const Box = styled(motion.div)`...`;

애니메이션 만들기

  • motion 컴포넌트의 prop을 넣어서 애니메이션을 적용합니다. (prop 값은 맘대로 정해도됨)
  • initial : 애니메이션이 들어가기 전 초기상태
  • animate : 애니메이션 효과
  • variants : initialanimate의 상태를 정리해놓은 오브젝트입니다.

https://www.framer.com/docs/transition/ 로 가면 다양한 애니메이션 효과들이 있습니다.

const variants = {
  hidden: { opacity: 0 },
  visible: { opacity: 1 },
}

return (
  <motion.div
    initial="hidden" 
    animate="visible"
    variants={variants}
  />
)

슬라이드를 만들자

임시 이미지를 넣은 데모 버전입니다. 전체 코드는 여기서 봐주세요❤

컴포넌트의 구조

  • 실질적으로 슬라이드를 담당하는 Box 컴포넌트가 1개밖에 들어있지 않습니다.
  • AnimatePresence를 이용하여 슬라이드를 만듭니다.
    import { AnimatePresence, motion } from "framer-motion"; import 해주세요!
  • custom prop을 이용하여 슬라이드 애니메이션의 방향을 바꿉니다.
    <Wrapper>
      <SlideWrap>
        <AnimatePresence custom={back}>
          <Box
            custom={back}
            variants={boxVariants}
            src={images[imageIndex]}
            initial="entry"
            animate="center"
            exit="exit"
            key={visible}
          />
        </AnimatePresence>
      </SlideWrap>
      <button onClick={prevPlease}>prev</button>
      <button onClick={nextPlease}>next</button>
    </Wrapper>
  • 사진 데이터들은 따로 image-data.js파일을 만들어서 이미지 주소들을 정리했습니다.
export const images = [
  "https://via.placeholder.com/250x250?text=1",
  "https://via.placeholder.com/250x250?text=2",
  "https://via.placeholder.com/250x250?text=3"
];

💙AnimatePresence

  • 자식 요소가 React 트리에서 제거될 때 애니메이션도 같이 제거해주는 컴포넌트입니다.
  • 즉, 요소를 애니메이션 효과와 함께 깔끔하게 퇴장시킬 수 있는 기능을 갖고 있습니다.
  • 기본 motion과는 달리 종료 애니메이션exit를 작성해야합니다.

슬라이드 박스에 데이터 넣기

  • 일단 custom prop을 지정하지 않고 작성했습니다.
  • 박스가 1개 밖에 없으므로 슬라이드를 구분하기 위해 key 값을 넣습니다.
  • visible은 버튼을 누를 때마다 1씩 증가 혹은 감소합니다.
  • popmotion의 wrap을 사용하여 박스마다 이미지를 적용합니다.
    wrap(min:number, max:number, v:number)
    wrap은 정의된 숫자 범위를 넘어서면 숫자를 다시 처음 상태로 바꿉니다.
import { wrap } from "popmotion";

const [visible, setVisible] = useState(0);
//박스마다 이미지 적용
const imageIndex = wrap(0, images.length, visible);
...

<Box
  variants={boxVariants}
  src={images[imageIndex]}
  initial="entry"
  animate="center"
  exit="exit"
  key={visible}
/>

버튼 만들기

  • 일단 custom prop을 지정하지 않고 작성했습니다.
  const nextPlease = () => {
    setVisible((prev) =>
      prev === images.length - 1 ? images.length - 1 : prev + 1
    );
  };
  const prevPlease = () => {
    setVisible((prev) => (prev === 0 ? 0 : prev - 1));
  };

      <button onClick={prevPlease}>prev</button>
      <button onClick={nextPlease}>next</button>

애니메이션 variants 작성하기

  • 일단 custom prop을 지정하지 않고 작성했습니다.
const boxVariants = {
  //등장 애니메이션 (애니메이션 진행률:0%)
  entry:{
    x: 500,
    opacity: 0,
    scale: 0
  },
  //메인 애니메이션 (50%, 슬라이드가 가운데로 왔을 때의 상태)
  center: {
    opacity: 1,
    x: 0,
    scale: 1,
    transition: { duration: 0.5 }
  },
  //종료 애니메이션 (100%)
  exit:{
    x: -500,
    opacity: 0,
    scale: 0,
    transition: { duration: 0.5 }
  }
};

중간체크!

next 버튼을 누를 때 오=>왼으로 잘 넘어가서 보기 좋은데
prev 버튼을 누르면 어딘가 어색해보입니다.

<AnimatePresence>custom prop을 이용해 prev 버튼을 눌렀을 때의 애니메이션 방향을 바꿔보겠습니다.

💜custom prop을 이용하여 애니메이션에 변주를 주기

  • useState를 이용하여 방향의 상태를 정합니다.
//방향 상태
  const [back, setBack] = useState(false);

//next 버튼 
  const nextPlease = () => {
    //next 때는 false
    setBack(false);
    setVisible((prev) =>
      prev === images.length - 1 ? images.length - 1 : prev + 1
    );
  };
//prev 버튼
  const prevPlease = () => {
    //prev 때는 true
    setBack(true);
    setVisible((prev) => (prev === 0 ? 0 : prev - 1));
  };
  • variants에 back을 인자로 받아 조건에 따른 애니메이션을 작성합니다.
    (back)=>({...}) 블록 괄호로 감싸주는 거 잊지 않기!
const boxVariants = {
  entry: (back: boolean) => ({
    //back일 때(prev 버튼을 눌렀을 때) 반대방향으로 
    x: back ? -500 : 500,
    opacity: 0,
    scale: 0
  }),
  center: {
    opacity: 1,
    x: 0,
    scale: 1,
    transition: { duration: 0.5 }
  },
  exit: (back: boolean) => ({
    //back일 때(prev 버튼을 눌렀을 때) 반대방향으로 
    x: back ? 500 : -500,
    opacity: 0,
    scale: 0,
    transition: { duration: 0.5 }
  })
};
  • <AnimatePresence>와 자식 요소에도 custom prop을 추가합니다.
<AnimatePresence custom={back}>
  <Box
    custom={back}
    variants={boxVariants}
    src={images[imageIndex]}
    initial="entry"
    animate="center"
    exit="exit"
    key={visible}
  />
</AnimatePresence>


끝입니다!! 참 쉽죠?

응용편(예고)

다음 포스트는 응용편으로 무한 슬라이드 + 모바일 해상도 한정 스와이퍼에 대해 작성하겠습니다.
코로나때문에 몸상태가 메롱이라서 한꺼번에 못쓰겟네요...ㅠㅠㅠ

응용편에 보여드릴 슬라이드입니다. 넷플릭스 클론인데 강의 내용 외에 몇몇 기능들을 추가했습니다.

📎참고

framer-motion 공식 홈페이지 - https://www.framer.com/motion/
슬라이드 예제 - https://codesandbox.io/s/pqvx3
wrap - popmotion - https://popmotion.io/popcorn/api/wrap/
React JS 마스터클래스 - 노마드코더 https://nomadcoders.co/react-masterclass

profile
공부중

2개의 댓글

comment-user-thumbnail
2023년 3월 1일

감사합니다! 덕분에 문제였던 부분을 해결했어요!!

답글 달기
comment-user-thumbnail
2024년 2월 3일

방향 바꿀 때 잘 안돼서 구글링 하고 있었는데 덕분에 해결했습니다 ㅠ 감사합니다!

답글 달기