본문 바로가기
Frontend/- Framer motion

Framer motion : Layout Animation에 대해 알아보자

by 코딩쥐 2024. 9. 28.

Framer motion의 Layout Animation은 UI 요소의 레이아웃 애니메이션을 부드럽게 전환할 수 있도록 돕는다. 아코디언 메뉴, 리스트 아이템의 추가 및 제거, 드래그 앤 드롭 동작에서의 애니메이션 등에서 사용한다.

  • layout
    개별 요소에 애니메이션 효과를 적용할 때 사용한다. 해당 요소의 크기나 위치가 변경될 때 자동으로 애니메이션이 적용된다. 

  • <LayoutGroup>
    여러 개의 요소를 그룹화(layoutId 사용)하여 함께 애니메이션 효과가 적용될 수 있도록 하는 컴포넌트다. 그룹 내에서 애니메이션 효과가 조화롭게 이루어진다.

 

layout 사용하기

개별 요소에 layout 속성을 삽입하면 자동으로 애니메이션이 적용된다. 아래 예제는 <motion.li>에 layout가 있을 때와 없을 때의 차이점이다. layout을 사용했을 때 자동으로 애니메이션이 적용된 모습을 볼 수 있다.

import { motion } from "framer-motion";
import styles from "./Comp01.module.css";
import { useState } from "react";

export default function Comp01() {
    const [lists, setLists] = useState(["list1"]);

    function addList() {
        setLists([...lists, `list${lists.length + 1}`]);
    }

    return (
        <div className={styles.container}>
            <button onClick={addList}>list 추가</button>
            <ul>
                {lists.map((list) => (
                //layout 속성 삽입
                    <motion.li layout key={list}>{list}</motion.li>
                ))}
            </ul>
        </div>
    );
}

 

<LayoutGroup> 사용하기

여러 개의 요소를 그룹화(layoutId 사용)하여 함께 애니메이션 효과가 적용될 수 있도록 하는 <LayoutGroup> 컴포넌트를 사용하여 아래 예제를 만들어보고자 한다. 메뉴 항목을 클릭할 때마다 선택된 메뉴 아래에 애니메이션이 적용된 밑줄이 부드럽게 이동하는 모습을 볼 수 있다.

 

1. Menu 컴포넌트를 생성한다.

Menu 컴포넌트의 밑줄 애니메이션을 그룹화 할 예정이므로 layoutId를 적용한다. 

// Menu.jsx
import { motion } from "framer-motion"

const Menu = ({ menu, isSelected, onClick }) => {
    return (
        <li onClick={onClick}
        style={{padding: 10}}>
            {menu}
            {isSelected ?
                <motion.div
                    // 밑줄 애니메이션을 그룹화
                    layoutId="underline"
                    style={{ height: 2, backgroundColor: "lightgray", borderRadius: 4, marginTop: 2 }} />
                : null}
        </li>
    )
}

export default Menu

 

2. Menubox 컴포넌트를 생성한다.

Menubox는 여러 <Menu/> 컴포넌트를 포함하고 있으며, 컴포넌트의 index를 사용하여 selectedMenu와 같을 경우에 isSelected가 true가 될 수 있도록 하였다. 

//Menubox.jsx

import Menu from "./Menu";

const Menubox = ({ menus, selectedMenu, onSelect }) => {
    return (
        <ul style={{ display: "flex", padding: 0, listStyle: "none"}}>
            {menus.map((menu, index) => (
                <Menu key={index}
                    menu={menu}
                    isSelected={selectedMenu === index}
                    onClick={() => onSelect(index)} />
            ))}
        </ul>
    )
}

export default Menubox;

 

3. <LayoutGroup/> 컴포넌트로 그룹화  

import { LayoutGroup } from "framer-motion";
import { useState } from "react";
import Menubox from "./css/Menubox";

export default function Comp01() {
    const [selectedMenu, setSelectedMenu] = useState(0);
    const menus = ['홈', '게시판', '방명록'];

    return (
        <LayoutGroup>
            <Menubox menus={menus} selectedMenu={selectedMenu} onSelect={setSelectedMenu}/>
        </LayoutGroup>
    )
}