Memperkenalkan Gerakan Pembingkai

Diterbitkan: 2022-03-10
Ringkasan cepat Animasi, jika dilakukan dengan benar, sangat kuat. Namun, membuat animasi yang menarik dengan CSS bisa jadi rumit. Masuklah Framer Motion. Dengan Framer Motion, Anda tidak perlu menjadi ahli CSS untuk membuat animasi yang indah. Framer Motion memberi kita animasi siap produksi dan API tingkat rendah yang dapat berinteraksi dengan kita untuk mengintegrasikan animasi ini ke dalam aplikasi kita.

Pada artikel ini, kita akan melihat lebih dekat bagaimana Framer Motion membantu kita dalam membuat animasi yang mengagumkan. Kita akan mempelajari cara kerja komponen gerak dan mempelajari cara merangkai animasi bersama-sama. Kita akan melihat cara membuat animasi yang dipicu gerakan, waktunya, dan gulir dengan gerakan Framer. Sepanjang jalan, kita akan menggunakan hal-hal yang kita pelajari untuk membangun lima aplikasi demo yang telah saya siapkan untuk menunjukkan kepada kita bagaimana kita dapat mengintegrasikan Framer Motion ke dalam aplikasi dunia nyata.

Tutorial ini akan bermanfaat bagi pembaca yang tertarik untuk mengintegrasikan animasi dalam aplikasi React mereka.

Catatan: Artikel ini membutuhkan pemahaman dasar tentang React dan CSS.

Apa itu Gerak Pembingkai?

Framer Motion adalah library animasi yang memudahkan pembuatan animasi. API yang disederhanakan membantu kami mengabstraksikan kerumitan di balik animasi dan memungkinkan kami membuat animasi dengan mudah.

Komponen Gerak

Ini adalah blok bangunan gerakan Framer. Komponen gerakan dibuat dengan mengawali motion ke elemen HTML dan SVG biasa Anda (misalnya, motion.h1 ). Komponen gerak dapat menerima beberapa props, dengan yang dasar adalah prop animate . Prop ini mengambil objek tempat kita mendefinisikan properti komponen yang ingin kita animasikan. Properti yang kita definisikan akan dianimasikan saat komponen dipasang di DOM.

Mari menganimasikan teks h1 menggunakan Framer Motion. Pertama, kita menginstal perpustakaan framer-motion dan mengimpor motion .

 npm i framer-motion import { motion } from 'framer-motion';

Kemudian kita mengubah h1 menjadi komponen gerak.

 <motion.h1 animate={{x: 20, y: -20}}> This is a motion component </motion.h1>

Ini akan menyebabkan h1 meluncur 20px ke kanan dan bergerak 20px ke atas saat dimuat. Saat unit tidak ditambahkan, perhitungan dilakukan menggunakan piksel. Namun, Anda dapat secara eksplisit mengatur unit yang Anda inginkan untuk menjadi dasar penghitungan, animate={{x: "20rem", y: "-20rem"}}> .

Lebih banyak setelah melompat! Lanjutkan membaca di bawah ini

Secara default, komponen gerakan akan dianimasikan dari status yang ditentukan dari gayanya ke yang ada di prop animate . Namun, jika kita mau, kita bisa membajak dan menentukan status animasi awal komponen menggunakan prop initial . Sementara prop animate digunakan untuk mendefinisikan perilaku komponen saat mereka me-mount, prop initial mendefinisikan perilaku mereka sebelum mereka mount.

Jika kita ingin h1 kita masuk dari kiri, kita mengontrolnya menggunakan prop awal.

 <motion.h1 initial={{x: -1000}} animate={{x: 20}}> This is a motion component </motion.h1>

Sekarang, ketika h1 dipasang, ia meluncur dari kiri.

Kami tidak terbatas pada satu animasi. Kita dapat mendefinisikan serangkaian animasi yang disebut keyframes dalam array nilai. Setiap nilai akan dianimasikan secara berurutan.

 <motion.h1 initial={{x: -1000}} animate={{x: [20, 50, 0, -70, 40] }}> This is a motion component </motion.h1>

Prop transition memungkinkan kita untuk menentukan bagaimana animasi terjadi. Dengannya, kami mendefinisikan bagaimana nilai bergerak dari satu keadaan ke keadaan lainnya. Antara lain, kita bisa menentukan duration , delay , dan type animasi menggunakan prop ini.

 <motion.h1 initial={{ x: -1000 }} animate={{ x: 0 }} transition={{ type: "tween", duration: "2", delay: "1" }}> This is a motion component </motion.h1>

Katakanlah kita menganimasikan beberapa komponen gerakan secara bersamaan, seperti pada cuplikan kode di bawah ini.

 <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>

Saat ini berfungsi, penyangga variants di Framer Motion memungkinkan kita untuk mengekstrak definisi animasi kita ke dalam objek varian. variants tidak hanya membuat kode kita lebih bersih, tetapi juga memungkinkan kita membuat animasi yang lebih kuat dan kompleks.

Mengekstrak definisi animasi kami ke dalam objek varian, kami memiliki ini:

 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" } }

Alih-alih meneruskan definisi animasi ke properti initial dan animate komponen secara langsung, kami mengekstrak definisi ini ke objek varian mandiri. Di objek varian, kami mendefinisikan nama varian yang mendeskripsikan nama setiap animasi sebagai varian.

 <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>

Di props variants , kami meneruskan nama objek varian untuk setiap komponen gerak dan kemudian meneruskan animasi ke props initial dan animate .

Kami dapat mengambil pengaturan kami saat ini dengan varian lebih lanjut untuk mengurangi pengulangan. Dengan menggunakan varian, kita dapat menyebarkan atribut animasi ke bawah melalui DOM dari komponen gerakan induk. Agar ini berfungsi, kami membuat varian untuk induk motion.div dengan nama animasi serupa di objek variannya sebagai turunannya. Dengan melakukan ini, kita tidak perlu meneruskan nama animasi 'ke setiap komponen anak. Di balik layar, elemen induk menanganinya untuk kita.

 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>

Sekarang kami memiliki kode yang lebih bersih tanpa pengulangan. Kami mengubah div container menjadi komponen gerak sehingga kami dapat meneruskan objek ContainerVariants yang kami definisikan. Karena kami tidak mendefinisikan animasi apa pun pada wadah, kami meneruskan objek kosong ke initial dan animate . Nama animasi Anda harus sama di setiap objek varian agar propagasi berfungsi.

Sekarang kita memahami dasar-dasar Framer Motion. Mari kita mulai membuat 5 aplikasi demo.

Toko Ikon

Kita dapat membuat animasi interaktif berdasarkan gerakan. Komponen gerakan saat ini dapat mendengarkan deteksi gerakan hover, tap, pan, dan drag. Kami akan membangun aplikasi Toko Ikon ini menggunakan prop whileHover .

Komponen

  • App.js : ini menampung teks heading.
  • Card.jsx : di sini, kita mendefinisikan animasi untuk kartu ikon.
  • CardContainer.jsx : kita mengimpor dan mengulang ikon.
  • styles.js : membuat, menata, dan mengekspor komponen gerak. Saya menggunakan komponen gaya untuk menata komponen.

Mari kita mulai dengan 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> );

Kami mengimpor komponen gerak H1 dan H2 yang kami buat di file Styles.js . Karena mereka adalah komponen gerak, kami menggunakan props initial dan props animate untuk menentukan perilaku mereka sebelum dan ketika mereka dipasang. Di sini, kami juga mengimpor dan menampilkan komponen CardContiner .

Sekarang, 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> ); };

Di sini, kita mengimpor SVG, komponen gerak Container , dan komponen Card .

Mirip dengan H1 dan H2 di App.js , kami mendefinisikan animasi Container menggunakan props initial dan props animate . Saat dimuat, itu akan menciptakan efek geser yang keren dari kiri browser.

Sekarang, 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> ); };

Di sini, kami membuat dua objek varian dengan animasi beforeHover dan onHover . Di objek CardVariants , kami tidak ingin melakukan apa pun pada awalnya, jadi beforeHover adalah objek kosong. onHover kami meningkatkan skala kotak kartu.

Dalam objek IconVariants , kita mendefinisikan status awal IconBox di beforeHover nya. Kami mengatur opacity ke 0 dan mendorongnya ke atas sebesar 50px. Kemudian, di onHover , kami mengatur opacity kembali ke 1, mendorongnya kembali ke posisi default, dan mengubah jenis transisi ke tween . Kemudian kami meneruskan varian ke komponen geraknya masing-masing. Kami menggunakan propagasi, jadi kami tidak perlu secara eksplisit mengatur props initial dan animate ke komponen IconBox .

Navbar Animasi

Kita akan membuat komponen Navigasi sederhana, dan kita akan melihat bagaimana kita dapat membuat hubungan pengaturan waktu antara komponen gerak induk dan anak.

Komponen

  • App.js : ini menampung teks heading.
  • Styles.js : membuat, menata, dan mengekspor komponen gerak. Komponen diberi gaya menggunakan komponen gaya.

Mari kita lihat file 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 } };

Kami membuat status isOpen yang akan digunakan untuk memeriksa apakah Navbar terbuka atau tidak. Kami membuat 3 objek varian, iconVariants , menuVariants , dan linkVariants di mana kami mendefinisikan animasi untuk komponen gerakan SvgBox , Nav , dan Link masing-masing. iconVariants digunakan untuk memutar SvgBox 135deg saat dilayangkan. Kita tidak perlu menambahkan "deg" ke nilainya. Di menuVariants , kami mengontrol posisi teratas Nav seperti Anda menggunakan properti position di CSS. Kami mengaktifkan posisi teratas Nav berdasarkan status isOpen .

Dengan varian, kita dapat membuat hubungan waktu antara komponen gerak induk dan anak. Kami mendefinisikan hubungan antara Nav induk dan anaknya, Link menggunakan properti when di objek transisi. Di sini, atur ke beforeChildren , sehingga animasi komponen induk akan selesai sebelum animasi anak dimulai.

Menggunakan properti staggerChildren , kami menetapkan urutan waktu untuk setiap tautan. Setiap tautan akan membutuhkan waktu 0,5 detik untuk muncul satu demi satu. Ini menciptakan isyarat visual yang bagus ketika Nav dibuka. Di linkVariants kami menganimasikan opacity dan posisi vertikal setiap link.

 <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>

Di sini, kami meneruskan varian ke komponennya masing-masing. Di SvgBox , kita beralih status isOpen setiap kali diklik, lalu menganimasikannya secara kondisional berdasarkan status. Seperti SvgBox , kami menganimasikan Nav dan Link secara kondisional berdasarkan isOpen .

Modal Animasi

Kami akan membangun komponen modal dan mempelajari tentang AnimatePresence Framer Motion, dan bagaimana elemen tersebut memungkinkan kami menganimasikan elemen saat mereka meninggalkan DOM.

Komponen:

  • App.js : kami menyiapkan status showModal di sini.
  • Modal.jsx : pekerjaan animasi yang sebenarnya terjadi di sini.
  • Styles.js : membuat, menata, dan mengekspor komponen gerak. Komponen diberi gaya menggunakan komponen gaya.

Mari kita lihat 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> ); }

Kami membuat status showModal yang akan digunakan untuk merender modal secara kondisional. Fungsi toggleModal akan mengubah status setiap kali ToggleButton diklik. ToggleButton adalah komponen gerak, jadi kita bisa mendefinisikan animasi untuknya. Saat dipasang, ia meluncur dari kiri. Animasi ini berjalan selama 0,5 detik. Kami juga meneruskan status showModal ke Modal melalui alat peraga.

Sekarang, 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>

Kami mengimpor AnimatePresence dari framer framer-motion . Ini memungkinkan kita untuk mengatur animasi keluar untuk komponen ketika mereka meninggalkan DOM. Kami merender Modal secara kondisional berdasarkan status showModal . Kami mendefinisikan animasi untuk ModalBox dan ModalContent melalui props initial dan animate mereka. Ada juga prop baru di sini, exit . Memiliki AnimatePresence sebagai pembungkus memungkinkan kita untuk menambahkan animasi keluar ke ModalBox di prop exit .

Gulir Animasi

Kami akan menggunakan kombinasi hook useAnimation dan react-intersection-observer untuk membuat animasi yang dipicu oleh scroll.

Komponen

  • App.js : kita menyiapkan animasi untuk komponen Box dan merendernya di App
  • Styles.js : membuat, menata, dan mengekspor komponen gerak. Komponen diberi gaya menggunakan komponen gaya.
 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} /> ); };

Kait useAnimation memungkinkan kita untuk mengontrol urutan di mana animasi kita terjadi. Kami memiliki akses ke metode controls.start dan controls.stop yang dapat kami gunakan untuk memulai dan menghentikan animasi secara manual. Kami meneruskan animasi hidden awal ke StyledBox . Kami meneruskan kontrol yang kami definisikan dengan metode start ke StyledBox animate prop.

kait useInView react-intersection-observer memungkinkan kita untuk melacak ketika sebuah komponen terlihat di viewport. Kait useInView memberi kita akses ke ref , yang kita teruskan ke komponen yang ingin kita tonton, dan boolean inView , yang memberi tahu kita apakah elemen itu inView atau tidak. Kami menggunakan useEffect untuk memanggil controls.start setiap kali elemen yang kami tonton, StyledBox terlihat. Kami meneruskan controls dan inView sebagai useEffect . Juga, kami meneruskan varian yang kami definisikan, BoxVariants ke StyledBox .

Animasi Pahlawan

Kami akan membuat animasi spanduk pahlawan yang keren menggunakan kait useCycle . Kita akan memahami bagaimana useCycle memungkinkan kita untuk menggilir animasi.

 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" }, }, };

Kami mendefinisikan 3 varian, H1Variants , TextVariants , dan BannerVariants . Namun, fokus kami adalah BannerVariants . Kami mendefinisikan 2 animasi, animationOne dan animationTwo di BannerVariants . Ini adalah animasi yang kami berikan ke useCycle untuk digilir.

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

useCycle bekerja mirip dengan kait useState . Dalam array yang didestruktur, animation mewakili animasi yang aktif, baik animationOne atau animationTwo . Fungsi cylceAnimation yang berputar di antara animasi yang kita definisikan. Kami meneruskan animasi yang ingin kami putar ke useCycle dan memanggil cylceAnimation setelah 2 detik di 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>

Di akhir segalanya, kami meneruskan varian ke komponen masing-masing dan menyaksikan keajaiban terjadi. Dengan ini, Banner awalnya akan meluncur dari kanan berdasarkan animasi yang kita definisikan di animationOne , dan setelah 2 detik, cycleAnimation akan dipanggil yang akan memicu animationTwo .

Seperti yang pernah dikatakan Babi yang bijak, "itu saja."

Kesimpulan

Kami telah mempelajari dasar-dasar Framer Motion dan melihat beberapa proyek demo yang memberi kami gambaran sekilas tentang berbagai animasi yang dapat kami buat. Namun, Anda dapat melakukan lebih banyak hal dengannya. Saya mendorong Anda untuk menyelami dokumen dan menjadi liar.

Sumber daya

  • Framer Motion Api Docs, Framer Motion
  • reaksi-persimpangan-pengamat, npm
  • Gerakan Pembingkai untuk React, NetNinja