라이브러리/framer-motion

[framer-motion] 페이지 전환 애니메이션

순코딩 2024. 8. 11. 11:58

아이템 우측 하단에 원을 클릭 시 해당 원의 크기가 커지며 화면을 덮는 애니메이션을 구현하였습니다.

import { motion } from "framer-motion";
import React, { useState } from "react";
import styled from "styled-components";

const Item = () => {
  // 원 클릭 여부 state
  const [isClick, setIsClick] = useState(null);

  return (
    // 아이템
    <Container>
      {/** 
       * 아이템 우측 하단 원
       * 클릭 시 isClick 상태가 변경된다.
       * animate는 isClick이 true일 경우 애니메이션 객체를 전달하도록 설정되어 클릭 시 아래 애니메이션이 진행된다.
      */}
      <Circle
        onClick={() => setIsClick(true)}
        animate={isClick && { width: "100dvw", height: "100dvh", top: "0px", left: "0px", borderRadius: "0px" }}
        transition={{ duration: 0.5, ease: "easeInOut" }}
      >
        {/**
         * 페이지 전환 후 보여줄 컨텐츠
         * 위와 마찬가지로 isClick이 true로 변경되면 애니메이션(컨텐츠가 서서히 나타남)이 진행된다.
         */}
        <InnerContent
          initial={{ opacity: 0 }}
          animate={isClick && { opacity: 1 }}
          transition={{ duration: 1, ease: "easeInOut" }}
        >
          hi
        </InnerContent>
      </Circle>
    </Container>
  );
};

// 아이템 컨테이너
const Container = styled.div`
  width: 200px;
  height: 50px;
  background-color: black;
  border-radius: 20px;
  position: relative;
`;

// 원
const Circle = styled(motion.div)`
  width: 30px;
  height: 30px;
  border-radius: 100%;
  background-color: red;
  position: absolute;
  bottom: -15px;
  right: -15px;
`;

// 원 내부 컨텐츠(페이지 전환 후 보여줄 컨텐츠)
const InnerContent = styled(motion.div)`
  font-size: 15px;
`;
export default Item;

 

 

 

디자이너의 충고를 받아들여버린 개발자의 코드(코드 리팩토링이라고 보시면 됩니다.)

 

import { motion } from "framer-motion";
import React, { useState } from "react";
import styled from "styled-components";

const Item = () => {
  // 원 클릭 여부 state
  const [isClick, setIsClick] = useState(null);

  return (
    // 아이템
    <Container>
      {/** 
       * 아이템 우측 하단 원
       * 클릭 시 isClick 상태가 변경된다.
       * animate는 isClick이 true일 경우 애니메이션 객체를 전달하도록 설정되어 클릭 시 아래 애니메이션이 진행된다.
      */}
      <Circle
        onClick={() => setIsClick(true)}
        animate={isClick && { 
            width: "100dvw",
             height: "100dvh",
              top: "0px",
               left: "0px",
                borderRadius:["100%", "20px", "0px"] // 키프레임
            }}
        transition={{ 
            duration: 0.8,
             ease: "easeInOut",
             times: [0, 0.2,  1],  // 각 keyframe의 타이밍

            }}
      >
        {/**
         * 페이지 전환 후 보여줄 컨텐츠
         * 위와 마찬가지로 isClick이 true로 변경되면 애니메이션(컨텐츠가 서서히 나타남)이 진행된다.
         */}
        <InnerContent
          initial={{ opacity: 0, fontSize:"60px" }}
          animate={isClick && { opacity: 1, fontSize:"100px" }}
          transition={{ duration: 0.32, ease: "easeInOut", delay:0.48 }}
        >
          Hello World!
        </InnerContent>
      </Circle>
    </Container>
  );
};

// 아이템 컨테이너
const Container = styled.div`
  width: 200px;
  height: 50px;
  background-color: black;
  border-radius: 20px;
  position: relative;
`;

// 원
const Circle = styled(motion.div)`
  width: 30px;
  height: 30px;
  border-radius: 100%;
  background-color: #ebc60e;
  position: absolute;
  bottom: -15px;
  right: -15px;
`;

// 원 내부 컨텐츠(페이지 전환 후 보여줄 컨텐츠)
const InnerContent = styled(motion.div)`
  font-weight:bold;
  color:white;
  width:100%;
  height:100%;
  display:flex;
  justify-content:center;
  align-items:center;
`;
export default Item;