フレーマーモーションの紹介

公開: 2022-03-10
クイックサマリー↬アニメーションは、正しく実行されると強力です。 ただし、CSSを使用して人目を引くアニメーションを作成するのは難しい場合があります。 入ってくるのはフレーマーモーションです。 Framer Motionを使用すると、美しいアニメーションを作成するためにCSSの専門家である必要はありません。 Framer Motionは、本番環境に対応したアニメーションと、これらのアニメーションをアプリケーションに統合するために対話できる低レベルのAPIを提供します。

この記事では、FramerMotionがすばらしいアニメーションの作成にどのように役立つかを詳しく見ていきます。 モーションコンポーネントがどのように機能するかを学び、アニメーションを連鎖させる方法を学びます。 フレーマモーションを使用して、ジェスチャでトリガーされ、タイミングを合わせてスクロールするアニメーションを作成する方法について説明します。 その過程で、私が設定した5つのデモアプリケーションを構築するために学んだことを使用して、FramerMotionを実際のアプリケーションに統合する方法を示します。

このチュートリアルは、Reactアプリケーションにアニメーションを統合することに関心のある読者にとって有益です。

注:この記事では、ReactとCSSの基本的な理解が必要です。

フレーマーモーションとは何ですか?

Framer Motionは、アニメーションの作成を簡単にするアニメーションライブラリです。 その簡素化されたAPIは、アニメーションの背後にある複雑さを抽象化し、アニメーションを簡単に作成できるようにします。

モーションコンポーネント

これらは、フレーマーモーションの構成要素です。 モーションコンポーネントは、通常のHTMLおよびSVG要素(例: motion.h1 )の前にmotionを付けることで作成されます。 モーションコンポーネントは複数のプロップを受け入れることができ、基本的なものはanimateプロップです。 このプロップは、アニメートするコンポーネントのプロパティを定義するオブジェクトを取り込みます。 定義するプロパティは、コンポーネントがDOMにマウントされるときにアニメーション化されます。

FramerMotionを使用して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が右に20ピクセルスライドし、ロード時に20ピクセル上に移動します。 単位が追加されていない場合、計算はピクセルを使用して行われます。 ただし、計算の基にする単位を明示的に設定できますanimate={{x: "20rem", y: "-20rem"}}>

ジャンプした後もっと! 以下を読み続けてください↓

デフォルトでは、モーションコンポーネントは、そのスタイルから定義された状態からanimateプロップの状態にアニメートされます。 ただし、必要に応じて、 initialプロップを使用して、コンポーネントの初期アニメーション状態をハイジャックして定義することができます。 animateプロップは、コンポーネントがマウントされるときのコンポーネントの動作を定義するために使用されますが、 initialプロップは、コンポーネントがマウントされる前の動作を定義します。

h1を左から入れたい場合は、最初の小道具を使用してそれを制御します。

 <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プロップを使用すると、アニメーションの発生方法を定義できます。 これを使用して、値が1つの状態から別の状態にアニメーション化する方法を定義します。 特に、このプロップを使用して、アニメーションのdurationdelay 、および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>

これで、繰り返しのない、よりクリーンなコードができました。 コンテナdivをモーションコンポーネントに変えて、定義したContainerVariantsオブジェクトを渡すことができるようにしました。 コンテナにアニメーションを定義しないため、空のオブジェクトをinitial化してanimateます。 伝播が機能するには、アニメーション名がすべてのバリアントオブジェクトで同じである必要があります。

これで、フレーマーモーションの基本を理解できました。 5つのデモアプリケーションの最初の作成を始めましょう。

アイコンショップ

ジェスチャーに基づいたインタラクティブなアニメーションを作成できます。 モーションコンポーネントは現在、ホバー、タップ、パン、およびドラッグジェスチャ検出をリッスンできます。 whileHoverプロップを使用してこのアイコンショップアプリを構築します。

コンポーネント

  • 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.jsH1H2と同様に、 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> ); };

ここでは、 onHover beforeHoverを使用して2つのバリアントオブジェクトを作成します。 CardVariantsオブジェクトでは、最初は何もしたくないので、 beforeHoverは空のオブジェクトです。 onHoverは、カードボックスのスケールを大きくします。

IconVariantsオブジェクトでは、 IconBoxbeforeHoverの初期状態を定義します。 不透明度を0に設定し、50px上に押し上げます。 次に、 onHoverで、不透明度を1に戻し、デフォルトの位置に押し戻し、トランジションタイプをtweenに変更します。 次に、バリアントをそれぞれのモーションコンポーネントに渡します。 伝播を利用するため、 IconBoxコンポーネントにinitialおよびanimateの小道具を明示的に設定する必要はありません。

アニメーションナビゲーションバー

単純なナビゲーションコンポーネントを作成し、親と子のモーションコンポーネント間のタイミング関係を作成する方法を確認します。

コンポーネント

  • 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状態を作成します。 3つのバリアントオブジェクト、 iconVariantsmenuVariants 、およびlinkVariantsを作成し、それぞれSvgBoxNav 、およびLinkモーションコンポーネントのアニメーションを定義します。 iconVariantsは、SvgBoxにカーソルを合わせたときにSvgBoxを135度回転させるために使用されます。 値に「度」を追加する必要はありません。 menuVariantsでは、CSSのpositionプロパティを使用するのと同じように、 Navの一番上の位置を制御します。 Nav状態に基づいて、 isOpenの上部の位置を切り替えます。

バリアントを使用すると、親と子のモーションコンポーネント間にタイミング関係を作成できます。 遷移オブジェクトの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秒間実行されます。 また、小道具を介して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を条件付きでレンダリングします。 ModalBoxModalContentのアニメーションは、 initial小道具とanimateの小道具を使用して定義します。 ここには新しい小道具もあります、 exit 。 ラッパーとしてAnimatePresenceを使用すると、 exitプロップのModalBoxに終了アニメーションを追加できます。

スクロールアニメーション

useAnimationフックとreact-intersection-observerの組み合わせを使用して、スクロールトリガーアニメーションを作成します。

コンポーネント

  • App.jsBoxコンポーネントのアニメーションを設定し、 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.stop controls.startにアクセスできます。 最初のhiddenアニメーションをStyledBoxに渡します。 startメソッドで定義したコントロールをStyledBoxに渡します。

react-intersection-observeruseInViewフックを使用すると、コンポーネントがビューポートに表示されるタイミングを追跡できます。 useInViewフックを使用すると、監視するコンポーネントに渡すrefと、その要素がinViewであるかどうかを通知するinViewブール値にアクセスできます。 useEffectを使用して、監視している要素であるStyledBoxが表示されているときはいつでも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" }, }, };

H1VariantsTextVariantsBannerVariantsの3つのバリアントを定義します。 ただし、私たちの焦点はBannerVariantsです。 BannerVariantsanimationOneanimationTwoの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で定義したanimationOneに基づいて右からスライドインし、2秒後にcycleAnimationが呼び出され、 animationTwoがトリガーされます。

賢明な豚がかつて言ったように、「それはすべての人々です」。

結論

Framer Motionの基本を理解し、作成できるアニメーションの範囲を垣間見ることができるいくつかのデモプロジェクトを見てきました。 ただし、これを使用するとさらに多くのことができます。 ドキュメントに飛び込んで、ワイルドになることをお勧めします。

資力

  • フレーマーモーションApiドキュメント、フレーマーモーション
  • react-intersection-observer、npm
  • Reactのフレーマーモーション、NetNin​​ja