Framer Motion ile tanışın

Yayınlanan: 2022-03-10
Hızlı özet ↬ Animasyonlar, doğru yapıldığında güçlüdür. Ancak, CSS ile göz alıcı animasyonlar oluşturmak zor olabilir. Framer Motion geliyor. Framer Motion ile güzel animasyonlar yapmak için CSS uzmanı olmanıza gerek yok. Framer Motion bize üretime hazır animasyonlar ve bu animasyonları uygulamalarımıza entegre etmek için etkileşim kurabileceğimiz düşük seviyeli bir API sağlıyor.

Bu makalede, Framer Motion'ın harika animasyonlar oluşturmamıza nasıl yardımcı olduğuna daha yakından bakacağız. Hareket bileşenlerinin nasıl çalıştığını öğreneceğiz ve animasyonları birlikte zincirlemeyi öğreneceğiz. Framer hareketiyle hareketle tetiklenen, zaman ayarlı ve kaydırmalı animasyonların nasıl oluşturulacağını inceleyeceğiz. Yol boyunca, Framer Motion'ı gerçek dünya uygulamalarına nasıl entegre edebileceğimizi göstermek için kurduğum beş demo uygulaması oluşturmak için öğrendiğimiz şeyleri kullanacağız.

Bu eğitim, animasyonları React uygulamalarına entegre etmekle ilgilenen okuyucular için faydalı olacaktır.

Not: Bu makale, temel bir React ve CSS anlayışı gerektirir.

Çerçeve Hareketi Nedir?

Framer Motion, animasyon oluşturmayı kolaylaştıran bir animasyon kitaplığıdır. Basitleştirilmiş API'si, animasyonların arkasındaki karmaşıklıkları soyutlamamıza yardımcı olur ve kolaylıkla animasyonlar oluşturmamıza olanak tanır.

Hareket Bileşenleri

Bunlar Framer hareketinin yapı taşlarıdır. Hareket bileşenleri, normal HTML ve SVG öğenize motion ön eki getirilerek oluşturulur (örneğin, motion.h1 ). Hareket bileşenleri, temel olan animate prop olmak üzere birkaç props'u kabul edebilir. Bu prop, canlandırmak istediğimiz bileşenin özelliklerini tanımladığımız bir nesneyi alır. Tanımladığımız özellikler, bileşen DOM'a bağlandığında canlandırılacaktır.

Framer Motion'ı kullanarak bir h1 metnini canlandıralım. İlk olarak framer- motion kitaplığını kuruyoruz ve motion'u içe aktarıyoruz.

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

Sonra h1'i bir hareket bileşenine dönüştürüyoruz.

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

Bu, h1 20 piksel sağa kaymasına ve yüklendiğinde 20 piksel yukarı hareket etmesine neden olur. Birimler eklenmediğinde, hesaplamalar pikseller kullanılarak yapılır. Ancak, hesaplamaların temel almasını istediğiniz birimleri açıkça belirleyebilirsiniz, animate={{x: "20rem", y: "-20rem"}}> .

Atlamadan sonra daha fazlası! Aşağıdan okumaya devam edin ↓

Varsayılan olarak, bir hareket bileşeni, stillerinden animate prop'takilere tanımlanan durumdan canlandırılacaktır. Ancak, istersek, initial pervaneyi kullanarak bileşenin ilk animasyon durumunu ele geçirebilir ve tanımlayabiliriz. animate prop, bileşenlerin monte edildiğinde davranışlarını tanımlamak için kullanılırken, initial prop, onların davranışlarını monte edilmeden önce tanımlar.

Eğer h1'imizin soldan gelmesini istiyorsak, bunu ilk pervaneyi kullanarak kontrol ederiz.

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

Şimdi, h1 takıldığında soldan kayar.

Tek bir animasyonla sınırlı değiliz. Bir dizi değerde keyframes adı verilen bir dizi animasyon tanımlayabiliriz. Her değer sırayla canlandırılacaktır.

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

transition pervanesi, animasyonların nasıl meydana geldiğini tanımlamamıza izin verir. Bununla, değerlerin bir durumdan diğerine nasıl hareket ettiğini tanımlarız. Diğer şeylerin yanı sıra, bu pervaneyi kullanarak animasyonun duration , delay ve type tanımlayabiliriz.

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

Aşağıdaki kod parçacığında olduğu gibi birkaç hareket bileşenini aynı anda canlandıracağımızı varsayalım.

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

Bu işe yararken, Framer Motion'daki variants prop, animasyon tanımlarımızı bir varyant nesnesine çıkarmamızı sağlar. variants yalnızca kodumuzu daha temiz hale getirmekle kalmaz, aynı zamanda daha güçlü ve karmaşık animasyonlar oluşturmamıza da olanak tanır.

Animasyon tanımlarımızı değişken nesnelere çıkardığımızda şuna sahibiz:

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

Animasyon tanımlarını doğrudan bir bileşenin initial ​​ve animate sahne öğelerine geçirmek yerine, bu tanımları bağımsız değişken nesnelere çıkarırız. Varyant nesnelerinde, her bir animasyonun adını varyantlar olarak tanımlayan varyant adları tanımlarız.

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

Prop variants , her bir hareket bileşeni için varyant nesnelerinin adını iletiyoruz ve ardından animasyonları initial ve animate props'a iletiyoruz.

Tekrarlamayı azaltmak için mevcut kurulumumuzu varyantlarla daha da ileri götürebiliriz. Varyantları kullanarak, animasyon niteliklerini bir ana hareket bileşeninden DOM üzerinden aşağıya doğru yayabiliriz. Bunun çalışması için, türev nesnesinde alt öğeleriyle benzer animasyon adlarına sahip üst motion.div için türevler oluştururuz. Bunu yaparak, her bir alt bileşene animasyon adlarını iletmemiz gerekmeyecek. Perde arkasında, ana öğe bunu bizim için halleder.

 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>

Artık tekrarı olmayan daha temiz bir kodumuz var. Tanımladığımız ContainerVariants nesnesine geçebilmek için container div'i bir hareket bileşenine çevirdik. Container üzerinde herhangi bir animasyon tanımlamadığımız için boş nesneleri initial ve animate . Yayılımın çalışması için animasyon adlarınızın her varyant nesnesinde aynı olması gerekir.

Şimdi Framer Motion'ın temellerini anlıyoruz. 5 demo uygulamamızdan oluşan yumruğumuzu oluşturmaya başlayalım.

Simge Dükkanı

Hareketlere dayalı etkileşimli animasyonlar oluşturabiliriz. Hareket bileşenleri şu anda üzerine gelme, dokunma, kaydırma ve sürükleme hareketi algılamayı dinleyebilir. Bu Icon Shop uygulamasını whileHover kullanarak oluşturacağız.

Bileşenler

  • App.js : Bu, başlık metinlerini tutar.
  • Card.jsx : Burada ikon kartlarının animasyonlarını tanımlıyoruz.
  • CardContainer.jsx : Simgeleri içe aktarır ve döngüye alırız.
  • styles.js : hareket bileşenlerini yaratın, şekillendirin ve dışa aktarın. Bileşenleri şekillendirmek için tarz bileşenleri kullandım.

App.js ile başlayalım.

 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 dosyasında oluşturduğumuz H1 ve H2 hareket bileşenlerini içe aktarıyoruz. Hareket bileşenleri oldukları için, initial ve animate desteklerini, monte edilmeden önceki ve ne zamanki davranışlarını tanımlamak için kullanırız. Burada ayrıca CardContiner bileşenini içe aktarıyor ve görüntülüyoruz.

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

Burada SVG'leri, Container hareket bileşenini ve Card bileşenini içe aktarıyoruz.

App.js H1 ve H2 benzer şekilde, initial ve animate sahne öğelerini kullanarak Container animasyonlarını tanımlarız. Yüklendiğinde, tarayıcının solundan içeri kaymanın harika bir etkisi yaratacaktır.

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

Burada, beforeHover ve onHover animasyonları ile iki değişken nesne oluşturuyoruz. CardVariants nesnesinde, başlangıçta hiçbir şey yapmak istemiyoruz, bu nedenle beforeHover boş bir nesnedir. onHover , kart kutusunun ölçeğini artırıyoruz.

IconVariants nesnesinde, IconBox içinde beforeHover ilk durumunu tanımlarız. Opaklığını 0'a ayarlayıp 50 piksel yukarı itiyoruz. Ardından, onHover opaklığı tekrar 1'e ayarlıyoruz, onu varsayılan konumuna geri itiyoruz ve geçiş türünü tween olarak değiştiriyoruz. Ardından varyantları ilgili hareket bileşenlerine aktarıyoruz. Yayılımdan yararlanıyoruz, bu nedenle IconBox bileşenine initial ​​ve animate sahne öğelerini açıkça ayarlamamıza gerek yok.

Hareketli Gezinme Çubuğu

Basit bir Gezinme bileşeni oluşturacağız ve ebeveyn ve çocuk hareket bileşenleri arasında nasıl zamanlama ilişkileri oluşturabileceğimizi göreceğiz.

Bileşenler

  • App.js : Bu, başlık metinlerini tutar.
  • Styles.js : hareket bileşenlerini oluşturun, şekillendirin ve dışa aktarın. Bileşenler, stilli bileşenler kullanılarak biçimlendirilir.

App.js dosyasına bir göz atalım.

 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'ın açık olup olmadığını kontrol etmek için kullanılacak bir isOpen durumu oluşturuyoruz. Sırasıyla SvgBox , Nav ve Link hareket bileşenleri için animasyonları tanımladığımız iconVariants , menuVariants , ve linkVariants olmak üzere 3 değişken nesne oluşturuyoruz. iconVariants , üzerine SvgBox 135deg döndürmek için kullanılır. Değere “derece” eklememize gerek yok. menuVariants , CSS'deki position özelliğini kullandığınız gibi Nav en üst konumunu kontrol ederiz. isOpen durumuna göre Nav ​​en üst konumunu değiştiririz.

Varyantlarla ebeveyn ve çocuk hareket bileşenleri arasında zamanlama ilişkileri oluşturabiliriz. Üst Nav ile alt öğesi arasındaki ilişkiyi, geçiş nesnesindeki when özelliğini kullanarak Link tanımlarız. Burada, onu beforeChildren olarak ayarlayın, böylece ana bileşenin animasyonları, çocuk animasyonu başlamadan önce bitecektir.

staggerChildren özelliğini kullanarak her bağlantı için bir zamanlama sırası belirledik. Her bağlantının birbiri ardına görünmesi 0,5 saniye sürecektir. Bu, Nav açıldığında güzel bir görsel ipucu oluşturur. linkVariants , her bir bağlantının opaklığını ve dikey konumunu canlandırıyoruz.

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

Burada varyantları ilgili bileşenlerine aktarıyoruz. SvgBox içinde, tıklandığında isOpen durumunu değiştiririz, ardından duruma göre koşullu olarak canlandırırız. SvgBox gibi, Nav ve Link isOpen durumuna göre koşullu olarak canlandırıyoruz.

Hareketli Modal

Kalıcı bir bileşen oluşturacağız ve Framer Motion'ın AnimatePresence ve DOM'dan ayrılan öğeleri canlandırmamıza nasıl izin verdiğini öğreneceğiz.

Bileşenler:

  • App.js : Burada showModal durumunu ayarlıyoruz.
  • Modal.jsx : Asıl animasyon çalışması burada gerçekleşir.
  • Styles.js : hareket bileşenlerini oluşturun, şekillendirin ve dışa aktarın. Bileşenler, stilli bileşenler kullanılarak biçimlendirilir.

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

Modalı koşullu olarak oluşturmak için kullanılacak bir showModal durumu oluşturuyoruz. toggleModal işlevi, ToggleButton tıklandığında durumu değiştirir. ToggleButton bir hareket bileşenidir, dolayısıyla onun için animasyonlar tanımlayabiliriz. Takıldığında, soldan içeri kayar. Bu animasyon 0,5 saniye boyunca çalışır. Ayrıca props aracılığıyla showModal durumunu Modal geçiriyoruz.

Şimdi, 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>

AnimatePresence framer-motion motion'dan içe aktarıyoruz. Bileşenler DOM'dan çıktıklarında çıkış animasyonları ayarlamamızı sağlar. Modal showModal durumuna göre koşullu olarak oluşturuyoruz. ModalBox ve ModalContent için animasyonları, initial ​​ve animate ModalContent aracılığıyla tanımlarız. Ayrıca burada yeni bir destek var, exit . AnimatePresence sarmalayıcı olarak kullanmak, exit pervanesinde ModalBox çıkış animasyonları eklememize izin verir.

Kaydırma Animasyonu

Kaydırma ile tetiklenen animasyonlar oluşturmak için useAnimation kancası ve react-intersection-observer bir kombinasyonunu kullanacağız.

Bileşenler

  • App.js : Box bileşeni için animasyonları kurduk ve App render ettik
  • Styles.js : hareket bileşenlerini oluşturun, şekillendirin ve dışa aktarın. Bileşenler, stilli bileşenler kullanılarak biçimlendirilir.
 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 kancası, animasyonlarımızın gerçekleştiği dizileri kontrol etmemizi sağlar. Animasyonlarımızı manuel olarak başlatmak ve durdurmak için kullanabileceğimiz controls.start ve controls.stop yöntemlerine erişimimiz var. İlk hidden animasyonu StyledBox . start metodu ile tanımladığımız kontrolleri StyledBox animate prop'a aktarıyoruz.

react-intersection-observer useInView kancası, görünüm alanında bir bileşenin ne zaman görünür olduğunu izlememizi sağlar. useInView kancası, izlemek istediğimiz bileşene ilettiğimiz ref öğesine ve bu öğenin inView olup olmadığını bize söyleyen inView boolean öğesine erişim sağlar. StyledBox , izlediğimiz öğe useEffect olduğunda controls.start çağırmak için kullanırız. controls ve inView useEffect 'in bağımlılıkları olarak iletiyoruz. Ayrıca, BoxVariants olarak tanımladığımız varyantları StyledBox .

Kahraman Animasyonu

useCycle kancasını kullanarak harika bir kahraman afiş animasyonu oluşturacağız. useCycle animasyonlar arasında geçiş yapmamızı nasıl sağladığını anlayacağız.

 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 ve BannerVariants olmak üzere 3 varyant tanımlıyoruz. Ancak, odak noktamız BannerVariants . BannerVariants 2 animasyon tanımlıyoruz, animationOne ve animationTwo . Bunlar, geçiş yapmak için useCycle animasyonlardır.

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

useCycle , useState kancasına benzer şekilde çalışır. Yapısı bozulmuş dizide, animasyon, animationOne veya animationTwo olsun, etkin olan animation temsil eder. Tanımladığımız animasyon arasında geçiş yapan cylceAnimation işlevi. Döndürmek istediğimiz animasyonları useCycle ve cylceAnimation 2 saniye sonra 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>

Her şeyin sonunda varyantları ilgili bileşenlerine aktarıyor ve sihrin gerçekleşmesini izliyoruz. Bununla, Banner , animasyonOne içinde tanımladığımız animationOne göre başlangıçta sağdan kayar ve 2 saniye sonra animationTwo tetikleyecek olan cycleAnimation çağrılır.

Bilge bir Domuzun bir zamanlar dediği gibi, "hepsi bu kadar."

Çözüm

Framer Motion'ın temellerini inceledik ve oluşturabileceğimiz animasyon çeşitleri hakkında bize bir fikir veren bazı demo projeleri gördük. Ancak, onunla çok daha fazlasını yapabilirsiniz. Sizi dokümanlara dalmaya ve vahşi olmaya teşvik ediyorum.

Kaynaklar

  • Framer Motion Api Dokümanları, Framer Motion
  • tepki-kavşak-gözlemci, npm
  • React, NetNinja için Framer Motion