React Framer Motion

Adam·2024년 4월 8일
0

react

목록 보기
7/7

리엑트앱에서 애니메이션을 잘 활용하게 된다면 아름다운 앱을 만드는 것이 더욱 편해진다.
이런 애니메이션을 편하게 하기위한 대표적인 패키지가 framer-motion이라는 패키지이다.
설치는 다음과 같이 진행을 하면 된다.

npm install framer-motion

framer motion을 사용하기 위해서는 기존 랜더링이 되던 html 컴포넌트, 예를 들어 div를 랜더링을 한다면 해당 컴포넌트를 motion.div로 변경을 해주면 된다.
만약에 styled-componenets를 사용해 커스텀화 된 html 컴포넌트를 랜더링하고 있다면 styled-component에 다음과 같이 motion을 추가해주면 된다.

const Box = styled(motion.div)`
    width: 200px;
    height: 200px;
    background-color: white;
    border-radius: 10px;
    box-shadow: 0 2px 3px rgba(0, 0, 0, 0.1), 0 10px 20px rgba(0, 0, 0, 0.06);
`;

그 후 해당 컴포넌트에 아래와 같이 animation을 추가해주면 된다.

function App() {
    return (
        <Wrapper>
            <Box transition={{duration:3}} animate={{borderRadius: "100px"}}/>
        </Wrapper>
    );
}

해당 앱을 실행 시켜본다면 3초 동안 네모난 상자가 borderRadius 100px인 원으로 변하는 것을 볼 수 있다.

Variants

위의 예시처럼 Html 컴포넌트 옆에 애니메이션을 전부 작성한다면 코드가 더러워질 수 있다.
이를 위해서 사용할 수 있는 것이 variants다.
먼저 myVariants라는 변수를 다음과 같이 만들어 준다.

const myVariant = {
    start: { scale: 0 },
    end: { scale: 1, rotateZ: 360, transition: { type: "spring", delay: 0.5 } },
};

그 후 Box 컴포넌트에 다음과 같이 적용을 해줄 수 있다.

<Box variants={myVariant} initial="start" animate="end"/>

적용할 variants를 지정해주면 해당 variant 컴포넌트를 순회하면서 이름에 맞는 스타일을 적용해준다.

Variants의 상속

부모 Variants의 속성을 자식 컴포넌트에 상속시키는 것 역시 가능하다.

const boxVariants = {
    start:{
        opacity:0,
        scale:0.5,
    },
    end:{
        opacity: 1,
        scale: 1,
        transition:{
            type:"spring",
            duration:0.5,
            bounce:0.5,
            delayChildren:0.5,
            staggerChildren:0.2,
        }
    },
};

const circleVariants = {
    start:{
        opacity:0,
        y:10,
    },
    end:{
        opacity:1,
        y:0,
    },
};

function App() {
    return (
        <Wrapper>
            <Box variants={boxVariants} initial="start" animate="end">
                <Circle variants={circleVariants}/>
                <Circle variants={circleVariants}/>
                <Circle variants={circleVariants}/>
                <Circle variants={circleVariants}/>
            </Box>
        </Wrapper>
    );
}

Box라는 부모 컴포넌트 밑에 Circle이라는 자식 컴포넌트 4개를 랜더링한다고 했을때, 자식 컴포넌트에서도 부모 컴포넌트의 start와 end 값을 별도로 지정하지 않아도 사용할 수 있다.
다만 당연하겠지만, variants내의 변수명은 부모와 동일하게 적용해야 자동적으로 적용이 가능하다.

또한 부모 variants에서 자식 컴포넌트들을 조종할 수 있는데, delayChildren을 통해 자식 컴포넌트가 부모의 애니메이션이 끝난 얼마 후 실행하게 할 수 있고, staggerChildren을 통해서 자식 컴포넌트 간 애니메이션을 얼마나 딜레이할지도 지정할 수 있다.

Drag

특정 컴포넌트를 드래그하고 싶은 경우가 있을탠데 이를 구현하는 것은 정말 간단하다.

<Box drag variants={boxVariants} whileHover="hover" whileTap="click"/>

해당 컴포넌트에 drag을 추가해주면 해당 box가 드래그 가능한 것을 볼 수 있다.

Animate Presence

컴포넌트가 없어지면서 애니메이션을 추가하고 싶을때 Animate Presence를 사용하면 된다.
예를 들어 버튼을 눌렀을때 box가 나오고 다시 버튼을 누르면 box가 사라지는 애니메이션을 구현하려고 한다.

const boxVariant = {
    initial: {
        opacity:0,
        scale:0,
    },
    visible: {
        opacity:1,
        scale:1,
        rotateZ: 360,
    },
    leaving: {
        opacity:0,
        y:50,
    },
}

function App() {
    const[showing, setShowing] = useState(false);
    const toggleShowing = () => setShowing((prev) => !prev);
    return (
        <Wrapper >
            <button onClick={toggleShowing}>Click</button>
            <AnimatePresence>{showing? <Box variants ={boxVariant} initial="initial" animate="visible" exit="leaving"/>: null}</AnimatePresence>
        </Wrapper>
    );
}

UseState을 사용해서 box의 보이는것의 상태를 확인하고 toggleShowing 함수를 통해 해당 상태값을 변경해준다.
그리고 해당 랜더링하는 부분을 AnimatePresence를 통해서 감싸주면 해당 객체가 없어질때도 정상적으로 애니메이션이 적용이 되는 것을 확인할 수 있다.

Layout

Framer-motion에서 layout이라는 것을 활용해서 컴포넌트간 애니메이션을 쉽게 구현할 수 있다.

const [clicked, setClicked] = useState(false);
    const toggleClicked = () => setClicked((prev) => !prev);
    return (
        <Wrapper onClick={toggleClicked}>
            <Box>
                {!clicked ? (
                    <Circle layoutId="circle" style={{ borderRadius: 50 }} />
                ) : null}
            </Box>
            <Box>
                {clicked ? (
                    <Circle layoutId="circle" style={{ borderRadius: 0, scale: 2 }} />
                ) : null}
            </Box>
        </Wrapper>
    );

useState을 통해 클릭이 됐는지 확인하는 상태를 만들어주고,
하나의 박스에는 클릭이 됐을때 원이 랜더링 되게, 하나의 박스는 클릭이 되지 않았을때 원이 랜더링 되지 않게 컴포넌트를 만들었다.
이때 박스 안에 랜더링 되는 원 컴포넌트 안에 layoutId를 동일하게 설정을 했을때 원이 박스 간에 랜더링이 되는 것을 확인할 수 있다.

profile
Keep going하는 개발자

0개의 댓글

관련 채용 정보