프레이머 모션 소개

게시 됨: 2022-03-10
빠른 요약 ↬ 애니메이션은 제대로 수행되면 강력합니다. 그러나 CSS로 눈길을 끄는 애니메이션을 만드는 것은 까다로울 수 있습니다. Framer Motion이 제공됩니다. Framer Motion을 사용하면 아름다운 애니메이션을 만들기 위해 CSS 전문가가 될 필요가 없습니다. Framer Motion은 프로덕션 준비 애니메이션과 이러한 애니메이션을 애플리케이션에 통합하기 위해 상호 작용할 수 있는 저수준 API를 제공합니다.

이 기사에서는 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" } }

애니메이션 정의를 구성 요소의 initialanimate 소품에 직접 전달하는 대신 이러한 정의를 독립형 변형 개체로 추출합니다. 변형 개체에서 각 애니메이션의 이름을 변형으로 설명하는 변형 이름을 정의합니다.

 <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 소품에서 각 모션 구성 요소에 대한 변형 개체의 이름을 전달한 다음 애니메이션을 initialanimate 소품에 전달합니다.

반복을 줄이기 위해 변형이 있는 현재 설정을 추가로 사용할 수 있습니다. 변형을 사용하여 상위 모션 구성 요소에서 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를 모션 컴포넌트로 바꿨습니다. 컨테이너에 애니메이션을 정의하지 않기 때문에 빈 객체를 initialanimate 에 전달합니다. 전파가 작동하려면 애니메이션 이름이 모든 변형 개체에서 동일해야 합니다.

이제 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 파일에서 만든 H1H2 모션 구성 요소를 가져옵니다. 모션 컴포넌트이기 때문에 initialanimate 소품을 사용하여 마운트 전과 마운트할 때의 동작을 정의합니다. 여기에서도 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.jsH1H2 와 유사하게 initialanimate 소품을 사용하여 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> ); };

여기에서는 beforeHoveronHover 애니메이션을 사용하여 두 개의 변형 개체를 만듭니다. CardVariants 개체에서 처음에는 아무 것도 하고 싶지 않으므로 beforeHover 는 빈 개체입니다. onHover 카드 상자의 크기를 늘립니다.

IconVariants 객체에서 IconBoxbeforeHover 의 초기 상태를 정의합니다. 불투명도를 0으로 설정하고 50px 위로 밀어 올립니다. 그런 다음 onHover 에서 불투명도를 다시 1로 설정하고 기본 위치로 다시 푸시하고 전환 유형을 tween 으로 변경합니다. 그런 다음 변형을 해당 모션 구성 요소에 전달합니다. 전파를 사용하므로 initialanimate 소품을 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 , NavLink 모션 구성 요소에 대한 애니메이션을 각각 정의하는 iconVariants , menuVariantslinkVariants 3개의 변형 개체를 만듭니다. iconVariantsSvgBox 위에 마우스를 올려 놓을 때 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 의 상태를 기반으로 NavLink 를 조건부로 애니메이션합니다.

애니메이션 모달

모달 구성 요소를 만들고 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 을 렌더링합니다. initialanimate 소품을 통해 ModalBoxModalContent 에 대한 애니메이션을 정의합니다. 여기에도 새로운 소품이 있습니다. 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.startcontrols.stop 메서드에 액세스할 수 있습니다. 초기 hidden 애니메이션을 StyledBox 에 전달합니다. StyledBox animate prop에 start 메소드로 정의한 컨트롤을 전달합니다.

react-intersection-observeruseInView 후크를 사용하면 뷰포트에 구성 요소가 표시되는 시점을 추적할 수 있습니다. useInView 후크는 우리가 보고 싶은 구성 요소에 전달하는 ref 및 해당 요소가 inView 인지 여부를 알려주는 inView 부울에 대한 액세스를 제공합니다. 우리는 우리가 보고 있는 요소, StyledBox 가 뷰에 있을 때마다 useEffect 를 사용하여 controls.start 를 호출합니다. controlsinViewuseEffect 의 종속성으로 전달합니다. 또한 정의한 변형 BoxVariantsStyledBox 에 전달합니다.

영웅 애니메이션

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 , TextVariantsBannerVariants 의 3가지 변형을 정의합니다. 그러나 우리의 초점은 BannerVariants 입니다. BannerVariantsanimationOneanimationTwo 라는 2개의 애니메이션을 정의합니다. 순환하기 위해 useCycle 에 전달하는 애니메이션입니다.

 const [animation, cycleAnimation] = useCycle("animationOne", "animationTwo"); useEffect(() => { setTimeout(() => { cycleAnimation(); }, 2000); }, []);

useCycleuseState 후크와 유사하게 작동합니다. 구조화되지 않은 배열에서 animationanimationOne 또는 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