프레이머 모션 소개
게시 됨: 2022-03-10이 기사에서는 Framer Motion이 멋진 애니메이션을 만드는 데 어떻게 도움이 되는지 자세히 살펴보겠습니다. 모션 구성 요소가 작동하는 방식과 애니메이션을 함께 연결하는 방법을 배웁니다. Framer 모션으로 제스처 트리거, 시간 제한 및 스크롤 애니메이션을 만드는 방법을 살펴보겠습니다. 그 과정에서 배운 내용을 사용하여 Framer Motion을 실제 응용 프로그램에 통합하는 방법을 보여주기 위해 설정한 5개의 데모 응용 프로그램을 구축할 것입니다.
이 튜토리얼은 React 애플리케이션에 애니메이션을 통합하는 데 관심이 있는 독자에게 도움이 될 것입니다.
참고: 이 기사는 React와 CSS에 대한 기본적인 이해가 필요합니다.
프레이머 모션이란?
Framer Motion은 애니메이션을 쉽게 만들 수 있는 애니메이션 라이브러리입니다. 단순화된 API를 통해 애니메이션 이면의 복잡성을 추상화하고 애니메이션을 쉽게 만들 수 있습니다.
모션 구성 요소
이것은 Framer 모션의 빌딩 블록입니다. 모션 구성 요소는 일반 HTML 및 SVG 요소(예: motion.h1
)에 motion
을 접두사로 붙여 생성됩니다. 모션 구성 요소는 animate
소품을 기본으로 하여 여러 소품을 허용할 수 있습니다. 이 소품은 애니메이션하려는 구성 요소의 속성을 정의하는 개체를 받습니다. 우리가 정의한 속성은 구성 요소가 DOM에 마운트될 때 애니메이션됩니다.
Framer Motion을 사용하여 h1 텍스트에 애니메이션을 적용해 보겠습니다. 먼저 framer-motion 라이브러리를 설치하고 motion
을 가져옵니다.
npm i framer-motion import { motion } from 'framer-motion';
그런 다음 h1을 모션 구성 요소로 변환합니다.
<motion.h1 animate={{x: 20, y: -20}}> This is a motion component </motion.h1>
이렇게 하면 h1
이 오른쪽으로 20px 미끄러지고 로드될 때 위로 20px 이동합니다. 단위가 추가되지 않으면 픽셀을 사용하여 계산이 수행됩니다. 그러나 animate={{x: "20rem", y: "-20rem"}}>
계산을 기반으로 하려는 단위를 명시적으로 설정할 수 있습니다.
기본적으로 모션 구성 요소는 스타일에서 animate
소품의 스타일로 정의된 상태에서 애니메이션됩니다. 그러나 원하는 경우 initial
소품을 사용하여 구성 요소의 초기 애니메이션 상태를 가로채고 정의할 수 있습니다. animate
소품은 마운트할 때 구성 요소의 동작을 정의하는 데 사용되지만 initial
소품은 마운트하기 전에 구성 요소의 동작을 정의합니다.
h1이 왼쪽에서 들어오게 하려면 초기 prop을 사용하여 제어합니다.
<motion.h1 initial={{x: -1000}} animate={{x: 20}}> This is a motion component </motion.h1>
이제 h1
이 마운트되면 왼쪽에서 미끄러집니다.
우리는 하나의 애니메이션에 국한되지 않습니다. 값 배열에서 keyframes
이라고 하는 일련의 애니메이션을 정의할 수 있습니다. 각 값은 순서대로 애니메이션됩니다.
<motion.h1 initial={{x: -1000}} animate={{x: [20, 50, 0, -70, 40] }}> This is a motion component </motion.h1>
transition
소품을 사용하면 애니메이션이 발생하는 방식을 정의할 수 있습니다. 이를 통해 값이 한 상태에서 다른 상태로 어떻게 움직이는지 정의합니다. 무엇보다도 이 소품을 사용하여 duration
, delay
및 애니메이션 type
을 정의할 수 있습니다.
<motion.h1 initial={{ x: -1000 }} animate={{ x: 0 }} transition={{ type: "tween", duration: "2", delay: "1" }}> This is a motion component </motion.h1>
아래 코드 조각과 같이 여러 모션 구성 요소를 동시에 애니메이션한다고 가정해 보겠습니다.
<div className="App"> <motion.h1 initial={{ x: -1000 }} animate={{ x: 0 }} transition={{ type: "tween", duration: "2", delay: "1" }}> This is a motion h1 </motion.h1> <motion.h2 initial={{ y: -1000 }} animate={{ y: 0 }} transition={{ type: "tween", duration: "1", delay: ".4" }}>This is a motion h2 </motion.h2> <motion.h3 initial={{ x: 100, opacity: 0 }} animate={{ x: 0, opacity: 1 }}> This is a motion h3 </motion.h3> <motion.h4 initial={{ scale: 0.7 }} animate={{ scale: 1.7 }} transition={{ type: "tween", duration: "2", delay: "1" }}> This is a motion h4 </motion.h4> </div>
이것이 작동하는 동안 Framer Motion의 variants
소품을 사용하면 애니메이션 정의를 변형 개체로 추출할 수 있습니다. variants
은 코드를 더 깔끔하게 만들 뿐만 아니라 훨씬 더 강력하고 복잡한 애니메이션을 만들 수 있게 해줍니다.
애니메이션 정의를 변형 개체로 추출하면 다음과 같습니다.
const H1Variants = { initial: { x: -1000 }, animate: { x: 0 }, transition: { type: "tween", duration: 2, delay: 1 } } const H2Variants = { initial: { y: -1000 }, animate: { y: 0 }, transition: { type: "tween", duration: 1, delay: .4 } } const H3Variants = { initial:{ x: 100, opacity: 0 }, animate:{ x: 0, opacity: 1 } } const H4Variants = { initial:{ scale: 0.7 }, animate:{ scale: 1.7 }, transition:{ type: "tween", duration: "2", delay: "1" } }
애니메이션 정의를 구성 요소의 initial
및 animate
소품에 직접 전달하는 대신 이러한 정의를 독립형 변형 개체로 추출합니다. 변형 개체에서 각 애니메이션의 이름을 변형으로 설명하는 변형 이름을 정의합니다.
<div className="App"> <motion.h1 variants={H1Variants} initial='initial' animate='animate' > This is a motion h1 </motion.h1> <motion.h2 variants={H2Variants} initial='initial' animate='animate' > This is a motion h2 </motion.h2> <motion.h3 variants={H3Variants} initial='initial' animate='animate' > This is a motion h3 </motion.h3> <motion.h4 variants={H4Variants} initial='initial' animate='animate' > This is a motion h4 </motion.h4> </div>
variants
소품에서 각 모션 구성 요소에 대한 변형 개체의 이름을 전달한 다음 애니메이션을 initial
및 animate
소품에 전달합니다.
반복을 줄이기 위해 변형이 있는 현재 설정을 추가로 사용할 수 있습니다. 변형을 사용하여 상위 모션 구성 요소에서 DOM을 통해 애니메이션 속성을 전파할 수 있습니다. 이 작업을 수행하려면 변형 개체에서 자식과 유사한 애니메이션 이름을 사용하여 부모 motion.div
에 대한 변형을 만듭니다. 이렇게 하면 애니메이션 이름을 각 자식 구성 요소에 전달할 필요가 없습니다. 무대 뒤에서 부모 요소가 이를 처리합니다.
const ContainerVariants = { initial: {}, animate: {} }; const H1Variants = { initial: { x: -1000 }, animate: { x: 0 }, transition: { type: "tween", duration: 2, delay: 1 } }; //more variants below <motion.div className="App" variants={ContainerVariants} initial="initial" animate="animate" > <motion.h1 variants={H1Variants}>This is a motion h1</motion.h1> <motion.h2 variants={H2Variants}>This is a motion h2</motion.h2> <motion.h3 variants={H3Variants}>This is a motion h3</motion.h3> <motion.h4 variants={H4Variants}>This is a motion h4</motion.h4> </motion.div>
이제 반복이 없는 더 깨끗한 코드가 있습니다. 우리는 우리가 정의한 ContainerVariants
객체를 전달할 수 있도록 컨테이너 div를 모션 컴포넌트로 바꿨습니다. 컨테이너에 애니메이션을 정의하지 않기 때문에 빈 객체를 initial
및 animate
에 전달합니다. 전파가 작동하려면 애니메이션 이름이 모든 변형 개체에서 동일해야 합니다.
이제 Framer Motion의 기본 사항을 이해했습니다. 5개의 데모 응용 프로그램을 만들기 시작하겠습니다.
아이콘 샵
제스처를 기반으로 대화형 애니메이션을 만들 수 있습니다. 모션 구성 요소는 현재 호버, 탭, 팬 및 드래그 제스처 감지를 수신할 수 있습니다. 우리는 whileHover
소품을 사용하여 이 Icon Shop 앱을 만들 것입니다.
구성품
-
App.js
: 제목 텍스트를 포함합니다. -
Card.jsx
: 여기에서 아이콘 카드의 애니메이션을 정의합니다. -
CardContainer.jsx
: 아이콘을 가져오고 반복합니다. -
styles.js
: 모션 구성 요소를 만들고 스타일을 지정하고 내보냅니다. 구성 요소의 스타일을 지정하기 위해 styled-components를 사용했습니다.
App.js
부터 시작하겠습니다.
import { H1, H2 } from "./Styles"; import CardContainer from "./CardContainer"; return ( <div> <H1 initial={{ y: -100 }} animate={{ y: 0, transition: { delay: 1 } }}> Icon Shop </H1> <H2 initial={{ x: -1000 }} animate={{ x: 0, transition: { delay: 1 } }}> Hover over the cards to see the motion magic </H2> <CardContainer /> </div> );
Styles.js
파일에서 만든 H1
및 H2
모션 구성 요소를 가져옵니다. 모션 컴포넌트이기 때문에 initial
및 animate
소품을 사용하여 마운트 전과 마운트할 때의 동작을 정의합니다. 여기에서도 CardContiner
구성 요소를 가져와서 표시합니다.
이제 CardContainer.js
.
import { Container } from "./Styles"; import Card from "./Card"; import { ReactComponent as AddIcon } from "./assets/add.svg"; import { ReactComponent as AirplaneIcon } from "./assets/airplane.svg"; import { ReactComponent as AlarmIcon } from "./assets/alarm.svg"; //more svg imports below... const icons = [ <AddIcon />, <AirplaneIcon />, <AlarmIcon />, //more icons below ]; const CardContainer = () => { return ( <Container initial={{ x: -1000 }} animate={{ x: 0 }}> {icons.map((icon) => ( <Card icon={icon} /> ))} </Container> ); };
여기에서 SVG, Container
모션 구성 요소 및 Card
구성 요소를 가져옵니다.
App.js
의 H1
및 H2
와 유사하게 initial
및 animate
소품을 사용하여 Container
의 애니메이션을 정의합니다. 로드되면 브라우저 왼쪽에서 슬라이딩하는 멋진 효과를 만듭니다.
이제 Card.js
import { CardBox, IconBox } from "./Styles"; const CardVariants = { beforeHover: {}, onHover: { scale: 1.1 } }; const IconVariants = { beforeHover: { opacity: 0, y: -50 }, onHover: { opacity: 1, y: 0, scale: 1.5, transition: { type: "tween" } } }; const Card = ({ icon }) => { console.log(icon); return ( <CardBox variants={CardVariants} initial="beforeHover" whileHover="onHover"> <IconBox variants={IconVariants}>{icon}</IconBox> </CardBox> ); };
여기에서는 beforeHover
및 onHover
애니메이션을 사용하여 두 개의 변형 개체를 만듭니다. CardVariants
개체에서 처음에는 아무 것도 하고 싶지 않으므로 beforeHover
는 빈 개체입니다. onHover
카드 상자의 크기를 늘립니다.
IconVariants
객체에서 IconBox
에 beforeHover
의 초기 상태를 정의합니다. 불투명도를 0으로 설정하고 50px 위로 밀어 올립니다. 그런 다음 onHover
에서 불투명도를 다시 1로 설정하고 기본 위치로 다시 푸시하고 전환 유형을 tween
으로 변경합니다. 그런 다음 변형을 해당 모션 구성 요소에 전달합니다. 전파를 사용하므로 initial
및 animate
소품을 IconBox
구성 요소에 명시적으로 설정할 필요가 없습니다.
애니메이션 Navbar
간단한 Navigation 구성 요소를 만들고 상위 모션 구성 요소와 하위 모션 구성 요소 간의 타이밍 관계를 만드는 방법을 살펴보겠습니다.
구성품
-
App.js
: 제목 텍스트를 포함합니다. -
Styles.js
: 모션 구성 요소를 생성, 스타일 지정 및 내보냅니다. 구성 요소는 styled-components를 사용하여 스타일이 지정됩니다.
App.js
파일을 살펴보겠습니다.
import { Header, Nav, Link, SvgBox } from "./Styles"; function App() { const [isOpen, setIsOpen] = useState(false); const iconVariants = { opened: { rotate: 135 }, closed: { rotate: 0 } }; const menuVariants = { opened: { top: 0, transition: { when: "beforeChildren", staggerChildren: 0.5 } }, closed: { top: "-90vh" } }; const linkVariants = { opened: { opacity: 1, y: 50 }, closed: { opacity: 0, y: 0 } };
Navbar가 열려 있는지 여부를 확인하는 데 사용할 isOpen
상태를 만듭니다. SvgBox
, Nav
및 Link
모션 구성 요소에 대한 애니메이션을 각각 정의하는 iconVariants
, menuVariants
및 linkVariants
3개의 변형 개체를 만듭니다. iconVariants
는 SvgBox
위에 마우스를 올려 놓을 때 135도 회전하는 데 사용됩니다. 값에 "deg"를 추가할 필요가 없습니다. menuVariants
에서 CSS의 position
속성을 사용하는 것처럼 Nav
의 상단 위치를 제어합니다. isOpen
상태를 기반으로 Nav
의 상단 위치를 토글합니다.
변형을 사용하여 상위 및 하위 모션 구성 요소 간의 타이밍 관계를 생성할 수 있습니다. 전환 개체의 when
속성을 사용하여 부모 Nav
와 자식 Link
간의 관계를 정의합니다. 여기에서 이를 beforeChildren
으로 설정하면 부모 구성 요소의 애니메이션이 자식의 애니메이션이 시작되기 전에 완료됩니다.
staggerChildren
속성을 사용하여 각 링크에 대한 타이밍 순서를 설정합니다. 각 링크가 차례로 나타나는 데 0.5초가 걸립니다. 이것은 Nav
가 열릴 때 멋진 시각적 신호를 만듭니다. linkVariants
에서 각 링크의 불투명도와 수직 위치에 애니메이션을 적용합니다.
<div className="App"> <Header> <SvgBox variants={iconVariants} animate={isOpen ? "opened" : "closed"} onClick={() => setIsOpen(!isOpen)} > <svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="https://www.w3.org/2000/svg" > <path d="M12 4C11.4477 4 11 4.44772 11 5V11H5C4.44772 11 4 11.4477 4 12C4 12.5523 4.44772 13 5 13H11V19C11 19.5523 11.4477 20 12 20C12.5523 20 13 19.5523 13 19V13H19C19.5523 13 20 12.5523 20 12C20 11.4477 19.5523 11 19 11H13V5C13 4.44772 12.5523 4 12 4Z" fill="#fff" /> </svg> </SvgBox> </Header> <Nav initial={false} variants={menuVariants} animate={isOpen ? "opened" : "closed"} > <Link variants={linkVariants}>home</Link> <Link variants={linkVariants}>about</Link> <Link variants={linkVariants}>gallery</Link> </Nav> </div>
여기에서 변형을 해당 구성 요소에 전달합니다. SvgBox
에서는 클릭할 때마다 isOpen
상태를 토글한 다음 상태에 따라 조건부로 애니메이션을 적용합니다. SvgBox
와 마찬가지로 isOpen
의 상태를 기반으로 Nav
및 Link
를 조건부로 애니메이션합니다.
애니메이션 모달
모달 구성 요소를 만들고 Framer Motion의 AnimatePresence
에 대해 배우고 DOM을 떠날 때 요소를 애니메이션으로 만드는 방법을 알아봅니다.
구성 요소:
-
App.js
: 여기에서showModal
상태를 설정합니다. -
Modal.jsx
: 실제 애니메이션 작업이 여기서 이루어집니다. -
Styles.js
: 모션 구성 요소를 생성, 스타일 지정 및 내보냅니다. 구성 요소는 styled-components를 사용하여 스타일이 지정됩니다.
App.js
를 살펴보겠습니다.
import { ToggleButton, Container } from "./Styles"; import Modal from "./Modal"; function App() { const [showModal, setShowModal] = useState(false); const toggleModal = () => { setShowModal(!showModal); }; return ( <Container> <ToggleButton initial={{ x: -700 }} animate={{ x: 0, transition: { duration: 0.5 } }} onClick={toggleModal} > Toggle Modal </ToggleButton> <Modal showModal={showModal} /> </Container> ); }
조건부로 모달을 렌더링하는 데 사용할 showModal
상태를 만듭니다. toggleModal
함수는 ToggleButton
을 클릭할 때마다 상태를 토글합니다. ToggleButton
은 모션 구성 요소이므로 이에 대한 애니메이션을 정의할 수 있습니다. 장착 시 왼쪽에서 밀어 넣습니다. 이 애니메이션은 0.5초 동안 실행됩니다. 또한 props를 통해 showModal
상태를 Modal
에 전달합니다.
이제 Modal.jsx
import { AnimatePresence } from "framer-motion"; import { ModalBox, ModalContent, Container } from "./Styles"; <Container> <AnimatePresence> {showModal && ( <ModalBox initial={{ opacity: 0, y: 60, scale: 0.3 }} animate={{ opacity: 1, y: 0, scale: 1, transition: { type: "spring", stiffness: 300 } }} exit={{ opacity: 0, scale: 0.5, transition: { duration: 0.6 } }} > <ModalContent initial={{ y: -30, opacity: 0 }} animate={{ y: 0, opacity: 1, transition: { delay: 1 } }} > Modal content!!!! </ModalContent> </ModalBox> )} </AnimatePresence> </Container>
framer-motion
에서 AnimatePresence
를 가져옵니다. 이를 통해 구성 요소가 DOM을 떠날 때 구성 요소에 대한 종료 애니메이션을 설정할 수 있습니다. showModal
상태를 기반으로 조건부로 Modal
을 렌더링합니다. initial
및 animate
소품을 통해 ModalBox
및 ModalContent
에 대한 애니메이션을 정의합니다. 여기에도 새로운 소품이 있습니다. exit
. AnimatePresence
를 래퍼로 사용하면 exit
소품의 ModalBox
에 출구 애니메이션을 추가할 수 있습니다.
스크롤 애니메이션
useAnimation
후크와 react-intersection-observer
의 조합을 사용하여 스크롤 트리거 애니메이션을 만듭니다.
구성품
-
App.js
:Box
구성 요소에 대한 애니메이션을 설정하고App
에서 렌더링합니다. -
Styles.js
: 모션 구성 요소를 생성, 스타일 지정 및 내보냅니다. 구성 요소는 styled-components를 사용하여 스타일이 지정됩니다.
import React, { useEffect } from "react"; import { useAnimation } from "framer-motion"; import { useInView } from "react-intersection-observer"; import { Container, H1,StyledBox } from "./Styles"; const BoxVariants = { visible: { opacity: 1, x: 0, transition: { duration: 1 } }, hidden: { opacity: 0, x: 300 }, }; const Box = () => { const controls = useAnimation(); const [ref, inView] = useInView(); useEffect(() => { if (inView) { controls.start("visible"); } }, [controls, inView]); return ( <StyledBox ref={ref} animate={controls} initial="hidden" variants={BoxVariants} /> ); };
useAnimation
후크를 사용하면 애니메이션이 발생하는 시퀀스를 제어할 수 있습니다. 애니메이션을 수동으로 시작하고 중지하는 데 사용할 수 있는 controls.start
및 controls.stop
메서드에 액세스할 수 있습니다. 초기 hidden
애니메이션을 StyledBox
에 전달합니다. StyledBox
animate prop에 start
메소드로 정의한 컨트롤을 전달합니다.
react-intersection-observer
의 useInView
후크를 사용하면 뷰포트에 구성 요소가 표시되는 시점을 추적할 수 있습니다. useInView
후크는 우리가 보고 싶은 구성 요소에 전달하는 ref
및 해당 요소가 inView
인지 여부를 알려주는 inView
부울에 대한 액세스를 제공합니다. 우리는 우리가 보고 있는 요소, StyledBox
가 뷰에 있을 때마다 useEffect
를 사용하여 controls.start
를 호출합니다. controls
과 inView
를 useEffect
의 종속성으로 전달합니다. 또한 정의한 변형 BoxVariants
를 StyledBox
에 전달합니다.
영웅 애니메이션
useCycle
후크를 사용하여 멋진 영웅 배너 애니메이션을 빌드합니다. useCycle
을 사용하여 애니메이션을 순환하는 방법을 이해할 것입니다.
import React, { useEffect } from "react"; import { useCycle } from "framer-motion"; import { Container, H1, HeroSection, Banner, TextBox } from "./Styles"; import { ReactComponent as BannerIllustration } from "./bighead.svg"; const H1Variants = { initial: { y: -200, opacity: 0 }, animate: { y: 0, opacity: 1, transition: { delay: 1 } }, }; const TextVariants = { initial: { x: 400 }, animate: { x: 0, transition: { duration: 0.5 } }, }; const BannerVariants = { animationOne: { x: -250, opacity: 1, transition: { duration: 0.5 } }, animationTwo: { y: [0, -20], opacity: 1, transition: { yoyo: Infinity, ease: "easeIn" }, }, };
H1Variants
, TextVariants
및 BannerVariants
의 3가지 변형을 정의합니다. 그러나 우리의 초점은 BannerVariants
입니다. BannerVariants
에 animationOne
및 animationTwo
라는 2개의 애니메이션을 정의합니다. 순환하기 위해 useCycle
에 전달하는 애니메이션입니다.
const [animation, cycleAnimation] = useCycle("animationOne", "animationTwo"); useEffect(() => { setTimeout(() => { cycleAnimation(); }, 2000); }, []);
useCycle
은 useState
후크와 유사하게 작동합니다. 구조화되지 않은 배열에서 animation
은 animationOne
또는 animationTwo
여부에 관계없이 활성화된 애니메이션을 나타냅니다. 우리가 정의한 애니메이션 사이를 순환하는 cylceAnimation
함수. 순환하려는 애니메이션을 useCycle에 전달하고 cylceAnimation
에서 2초 후에 useCycle
을 호출 useEffect
.
<div className="App"> <Container> <H1 variants={H1Variants} initial="initial" animate="animate"> Cool Hero Section Anmiation </H1> <HeroSection> <TextBox variants={TextVariants} initial="initial" animate="animate"> Storage shed, troughs feed bale manure, is garden wheat oats at augers. Bulls at rose garden cucumbers mice sunflower wheat in pig. Chainsaw foal hay hook, herbs at combine harvester, children is mallet. Goat goose hen horse. Pick up truck livestock, pets and storage shed, troughs feed bale manure, is garden wheat oats at augers. Lamb. </TextBox> <Banner variants={BannerVariants} animate={animation}> <BannerIllustration /> </Banner> </HeroSection> </Container> </div>
모든 것이 끝나면 변형을 해당 구성 요소에 전달하고 마법이 일어나는 것을 지켜봅니다. 이를 통해 Banner
는 처음에 animationOne
에서 정의한 애니메이션을 기반으로 오른쪽에서 슬라이드 인되며 2초 후에 animationTwo
를 트리거하는 cycleAnimation
이 호출됩니다.
현명한 돼지가 말했듯이 "그게 다야."
결론
우리는 Framer Motion의 기본 사항을 살펴보았고 우리가 만들 수 있는 애니메이션의 범위를 엿볼 수 있는 데모 프로젝트를 보았습니다. 그러나, 당신은 그것으로 훨씬 더 많은 것을 할 수 있습니다. 나는 당신이 문서에 뛰어들고 야생으로 나가기를 권장합니다.
자원
- 프레이머 모션 API 문서, 프레이머 모션
- 반응 교차 관찰자, npm
- React용 Framer Motion, NetNinja