Framer Motion ile tanışın
Yayınlanan: 2022-03-10Bu 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"}}>
.
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
: BuradashowModal
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 veApp
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