Framer Motion

Framer Motion

https://www.framer.com/motion/

Bu haftaki konumuz framer-motion kütüphanesi. Framer-motion javascriptin gücünü kullanarak animasyonlar oluşturmamızı yarayan ve community’si en güçlü librarylerden bir tanesidir. Alternatif olarak react-spring öneririm.

Anlatmaya başlamadan önce belirmek isterim ki gif şeklinde çıktılar almak çok fazla zaman kaybı yarattığı için anlattığım örnekler için gif ile sonucu gösterme veya örnek verme gibi çıktılar size gösteremeyeceğim. O yüzden tüm örnekleri deneyerek ve açıklamamı okuyarak ilerlerseniz bu makale amacına ulaşmış olacaktır.

Framer Motion Documentation API

https://www.framer.com/api/motion/

A Lot Examples of Framer Motion

Codesandbox’da geliştirilmiş birbirinden harika bu örneklere bakmayı unutmayın. Click here.

Kurulum

npm install --save framer-motion

motion

  • bu methodla framer motion librarysini kullanarak animasyon eklenebilir elementler oluşturabiliriz. motion.div motion.h1 gibi.
  • Ve aldığı bazı özel attributesler/propslar vardır. Bunları aşağıda ki basit örnekte göreceksiniz.

initial: İçerisine obje olarak başlangıç yani initial olarak taşımasını istediğimiz ayarları veririz.

animate: Initial ile aynı şekilde içerisine değerler alır. Animate ise initialdan sonra alması gereken değerleri alır. Yani initial olarak açılacak ve daha sonra animatedeki ayarlara dönecek ve bu dönüşümden animasyonumuz oluşacak.

transition: Burada ise animasyonumuz ile ilgili genel transition ayarlarını yapıyoruz. Nasıl ve ne gibi derseniz, animasyonun gerçekleşeceği süre ve type gibi genel özellikler. Veya istersek initial, animate veya exit içinde kendilerine özel transition değerleri belirleyebiliriz.

Actions with Inputs

Bu kısımda da animation efektlerimizin inputlarla ve state ile etkileşimleriyle yapabileceğimiz şeyleri göreceğiz.

  • Aşağıda bir buton ve butonla birlikte kullanacağımız bir isToggle statesi oluşturduk. Butona tıkladığımızda state 1 veya 0 şeklinde değişiyor. 1 olduğunda motion ile tanımladığımiz h2nin opacity 1 oluyor, 0 olduğunda opacity 0 oluyor ve bu değişimlerde motion animte çalışarak bizim geçişleri animasyonlu bir şekilde görmemizi sağlıyor.
  • Diğerinde ise range inputu ve bir başka state tutuyoruz. Burada ise range inputunu sağa ve sola sürüklediğimiz de state değisiyor ve stateye göre h2 elementimizin animatedeki x ekseninde ki koordinatı değişiyor ve buna göre animasyonlu bir şekilde hareket alıyor.

AnimatePresence & exit

AnimatePresence bir Wrapper componentdır. Yok olacak elementin çevresini sarar. Yani React DOM agacından kaldırılacak elemanların çevresini sarar.

framer-motion dan aldığımız bu component ile, Elementler veya Componentler unmount olurken yani yok olurken animasyonlar oluşturabiliriz.

Ve yok olacak motion elementinin exit attributesi olması gerekmektedir. Bunun içinde motion elementlerine aşagidaki gibi exit propsu içinde alması gereken animasyon özellikleri veririz.

exit: Elementin yok olmadan önceki alacağı bir motion element attributesidir.

exitBeforeEnter

  • Bir AnimatePresence propsudur. Verdiğimiz yerin içindeki motion.div lerinin exit animationlarını bitirmesini ve daha sonra gelen animationun sonra başlamasını sağlar.

Keypoints

  • framer-motion kütüphanesinde ki keypointler aslında bizim css animationlarda ki, key point noktaları gibidir. Örnek üzerinde açıklamak daha mantıklı olacakdır.
  • Keypointleri array içinde değerler olarak tanımlarız. Yani normalde opacity: 1 diyerek tek değer atayacağımıza, opacity: [1,0,1,0,0,1…] diye istediğimiz kadar değer atayabiliriz. Ve bunlarda bizim animasyonda ki keyframes içinde ki keypoint noktalarımızdır aslında. Tabi bunlar geçerli değerler olmalı opacity’e 2 vermek mesela geçerli bir değer olmaz çünkü cssde opacity 1 veya 0 olmalıdır.
  • Ve birde aşağıda ki örnekte times var gördüğünüz gibi. Buda duration’un opacity’deki keypointlere nasıl bölüştürüleceğini tanımlar. Mesela aşağıda ki örnekte 0.3 yani durationun %30’u opacity’deki ilk elemanın olsun demişiz, 0.3–0.6 arası yani yine %30'u opacitynin 2. elemanı 0 keypointi için olsun demişiz. Ve 0.6’dan 1 olana kadar yani %60 dan %100’e kadar kalan %40'lık kısımda opacity’nin 3. elemanı 1'in olsun demişiz.

Variants

  • Variantslar bizi kod tekrarından kurtaran framer-motion yapılarıdır.
  • İlk önce aşağıda ki gibi bir variants adında veya farklı bir isimlede olabilir, bir değişkeni obje olarak tanımlıyoruz ve içerisine istediğimiz isimlerde object key isimleri belirleyebiliyoruz. Ben accordion yapım için open ve closed kullandım. Daha sonra bu keylere value olarak, initial, animate veya exitte kullandığımız gibi değerleri veriyoruz.
  • Daha sonra bu tanımladığımız objeyi variants propsu olarak motion’a veriyoruz.
  • Ve daha sonra aşağıda ki gibi initial, animate veya exit icin tek yapmamız gereken variants propsuna verdiğimzi objedeki key isimlerini string olarak vermek.
  • Ve tadaaa animasyonumuz halen eskisi gibi çalışıyor. Hem daha okunaklı hem de daha pratik.

Styled-components ile Birlikte Kullanmak

  • framer-motionu istersek styled-components ile birlikte de kullanabiliriz. Bu şekilde kullanmak için styled.div yapmak yerine styled’a argument olarak motion.div yaparız. Aşağıda örnek olarak bir Nav elemanının bir styled-components ile birde motion-framer ile birlikte yapılışını örnek olarak bırakıyorum.

Nav Menu Animation

  • Elimizde aşağıda ki gibi bir menümüz var. Burada Headerin yanında ki logoya basınca sayfayı kaplayan, sayfaya soldan giren bir menu tasarlamak istiyoruz.

· Burada ki gibi. Ama sayfayı tam kaplayacak 100vw 100vh yani.

  • Aşağıya ana App.js imi ayarlıyorum.
  • Menüye onClick veriyorum ki Headerin yanında ki logoya tıklayınca state değişsin ve o stateye göre menu açılsın.

  • Burada da Menu componentım bulunuyor. {…props} yaparak onClick’i dive atıyorum aslında.

  • Ve son olarak da burada Nav componentım bulunuyor.

  • Burada Nav’in kapalı olduğunda, DOM’dan tamamen kaldırılmış sekilde olmasını istemediğim icin AnimatePresence Wrapperını kullanmıyorum. Onun yerine animate’e isNavOpen statesine gore variantda ki open veya closed olmasını istiyorum. Yani sadece opacitysi olmuyor, kapalı olduğunda ama her zaman DOMda bulunuyor.
  • Ayrıca aşağıda gördüğünüz üzere her animate, initial ve exit durumları için transition özelliğini içlerinde de istersem tanımlayabiliyorum.
  • İstersem hem Menu Nav için hemde motion.li ler için aynı variant kullanabilirdim. Fakat burada ayri varianlar kullandım. Bu sayede motion.li lerin ayrı animationlara sahip olmasını sağladım. Eğer kullanmasaydım motion.li ler MenuNavla beraber aynı animationları uygulayacaklardı.
  • Peki niye farklı animasyonlar uyguladık, bu animasyonlar bize ne sağlayacak. Aşağıda liVariant yani motion.li’ye verdiğimiz variantda ki open’da .3 saniye delay yani gecikme var. Openla birlikte hemen animasyonu çalışan MenuNav açılır ve daha sonra liVariants daki .3 yani 0.3 saniye gecikmeyle Navdaki linin, liVariants daki open çalışır. Yani kısaca bu delay sadece linkler için uygulanır ve bu sayede bizim linklerimiz MenuNav geldikten 0.3 saniye sonra ekrana animasyonlu bir şekilde giriş yapar.
  • Ve variantsdada bir .3 saniye gecikme var gördüğünüz gibi. Buda closed animation çalıştığın da, bizim linklerimizde delay olmadığı için hemen çıkış yaparlar. Fakat variantsda MenuNav için bir gecikme olduğu için MenuNav komponentı 0.3 saniye gecikmeyle animasyonları çalışarak yok olur.

staggerChildren & delayChildren & staggerDirection & when

  • Transition ve delayi liVariants lardan kaldırıyoruz. Ve ul variants oluşturuyoruz. Ve jsx deki ul’yi de motion.ul şeklinde yapıyoruz.
  • Ve ul’e open çalıştığında scale 1.05 yapıyoruz. Bu tüm ul ve içindekileri 1.05 kadar büyütecektir. Closedda da tekrar 1 olsun diyoruz yani normal boyutları.
  • Transition tanımlıyoruz.
  • staggerChildren diyerek 0.5 diyoruz. Bu bize ul nin içindeki nav linki li elemanlarının yani childrenlarının 0.5 saniye aralıklarla sırayla sahneye animasyonlu şekilde girmesini sağlıyor.
  • delayChildren tüm childrenların gecikme süresidir. Burada 2 saniye demişiz yani childrenların sırayla girmeye başlamasi, ul open olduktan 2 saniye sonra gerçekleşecektir.
  • straggerDirection ise ya 1 olur default değeridir. Yada -1 olur. -1 yaparsak normalde en üsttekinden başlıyarak giriyorsa sahneye bu sefer ul içindeki li elemanlarinin en sonundakinden baslayarak sahneye girecegi için tersten bir animasyon efektini başlatmış oluruz ul icindeki li ler için.
  • When ise ya afterChildren yada beforeChildren olur. Peki bu ne için? When transition içinde yazıldığı için, Transition dışında ki diğer animationların, transitionda ki animasyonlardan sonra çalışmasını sağlar. Yani afterChildren ile ilk önce transition içinde ki animasyonlar çalışır ulde daha sonra scale çalışarak 1.05 büyütme uygular. BeforeChildren olsaydı 1.05 büyütme uygulardı ve daha sonra transitiondaki animasyonlar çalışırdı.

Gestures

  • Gesturesler motion-framer’in fakeyle uzerinde gezme, dokunma, kaydirma, surukleme hareket izleyicileri ve olay dinleyicileridir. Bunlar bize yardimci ozelliklerdir diyebiliriz.
  • whileHover hover oldugumuz zaman olacak framer efektlerini tanimlariz.
  • whileTap, Card elementine dokundugumuzda gerceklesecek efekt ve animasyonlardir.
  • onHoverEnd motion framerin bize verdigi eventdir. Mouse ile hoverdan ciktigimizda calisacakdir.

drag & dragConstraints

· drag verdigimiz elementi drag edilebilir yani suruklenebilir hale getiriyor.

· dragConstraints ise verdigimiz elementin browserdaki windows size’ina gore limitler belirliyor. Ornegin asagida Card elementinin maksimum top yani ust kisimda 100px’e kadar suruklenebilir. Siz mouse ile disariya suruklesenizde, biraktiginizda o geriye donup toptan 100 px asagiya gelecektir.

  • drag’a istersek x veya y stringi verebiliriz bu sayede suruklenebilirligi x veya y ekseniyle sinirlayabiliriz.
  • Asagida element dragini dragda x ekseninde sinirlandiriyoruz, daha sonra dragConstraints ilede left ve right kisimlardan disariya cikmamasi icin ayrica birdaha sinirlandiriyoruz. Bu sayede itemimiz saga ve sola surtuklenebiliyor ve ayrica width den disariyada cikamiyor olacak.

useMotionValue & useTransform

  • x ve opacity constant larinin ciktisi asagidaki resimdeki gibidir.
  • useMotionValue ile initiali 0 olan bir argument veriyoruz ve takip edilebilen bir x yapiyoruz.
  • Daha sonra da useTransform yaparak takip edilen useMotionValueyi burada useTransfrom’da ilk argument olarak veriyoruz, ikincxi argument olarakda kordinatlari veriyoruz ve 3. Argument olarakda 2. Argumentde verdigimiz kordinatlarda hangi ozelliklerde olmasini istiyoruz. Opacity’e bu degiskeni verecegimiz icin opacity nin Kabul ettigi 0 ve 1 I kullanarak, -200 icin yani soladogru kaydirildiginda 0(opacity 0 olacak ayni sola kaydirildikca yok olacak), ortadayken yani 0 da initialdayken opacity 1 olacak ve 200 deyken yani saga kaydirildigindada opacity 0 olacak ve yine yok olacak seklinde ayarliyoruz.

Saga Kaydirinca Yok Olmasini Saglamak

  • Yukarida ki ornekte hazirladigimiz x ekseninde hareket eden karti saga kaydirilinca yok olmasini saglamak istiyoruz asagidaki gibi.

onDragEnd

  • Sadece opacity 0 olmayacagi ayni zamanda DOM’dan da silinmesini istedigimiz icin AnimatePresence wrapperimizida import edecegiz.
  • Resime tiklayip saga kaydirdigimiz zaman aslinda bir drag eventi olusturmus oluyoruz ve biz bu dragin sonunda eger kartimiz belirledigimiz 200px kadar saga veya sola acildiysa kartin silinmesini istiyoruz. Bunun icin exit eventi ayarlamamiz gerekiyor.
  • Exit eventini bir state’de tutacagim. State isCardVisible. Ne zaman ki Card’in x degeri 200 veya -200 olursa bu stateyi false yapacagim ve exit animasyonu calisacak. Fakat exit animationunu Card’a uygulayamam cunku eger Carda exit animation uygularsak, Card saga surukleme eventi bittiginde tekrar geri merkeze donme animasyonu calisip daha sonra silinme animasyonu exit ile calisacagi icin asagida ki gibi bir bug ile karsilasiriz.

  • Bu sorunu cozmek icin ekstra bir motion.div ile Wrap ediyoruz cevresini ve exit animationumuzu burada tanimliyoruz.
  • Peki Card’in x ekseninde 200 saga veya sola acildigini nasil anliyor ve takip ediyoruz. onDragEnd eventi 2 parametre veriyor bize. Event ve info. Event tetiklendiginde Info.point x ve y keylerini ve valuelarini donuyor bize. Burada ki x ve y belirledigimiz 0 yani merkeze uzakliklarini soyler. Merkezi pointi farkli soyleseydik ona gore deger donecek ve hareket edecekti.

Project: Shuffle Component

  • Bu ornegimizde asagidaki gibi bir component olusturacagiz ve shuffle butonuna tikladigimizda icindeki renkli kutulu divler, her seferinde karisip farkli siralanacaklar.
  • Bu ornegimizde lodash kullanacagimiz icin lodashida asagida ki gibi yukluyoruz.

npm install --save lodash

Layout

  • Layout ozelligi verdigimiz elementin otomatik animasyon kazanmasini saglar. Diyelim bir butona tikladigimizda kutu sayfanin en ustunden en altina gidecek. Kutu div oldugunu var sayalim. Normalde tikladigimizda en ustten en alta birden animasyonsuz keskin bir sekilde gecer. Fakat div’I motion.div’e cevirdikten sonra layout props unu verirsek eger artik kutu yaptigi o islemi framer-motion un default animation ozellikleriyle yapar. Isterseniz tabi ayarlarini control edebilrisiniz.
  • Bir state olusturuyoruz ve bu stateye colorslarimizin oldugu arrayi veriyoruz ve bu stateyi .map ile return ederken mapden gelen elemanlari motion.div ile return ediyoruz.
  • Ve her bir motion.div ayni zamanda da keye sahip olmali yoksa shuffle calismaz cunku onlari taniyamaz.
  • Lodash’dan shuffle methodunu import ediyoruz. Bu array elemanlarini karistirip tekrar array donen bir method.
  • Ve her bir motion.div layout propsuna sahip olmali yoksa event animation calismaz. Burda karistirilirken gozumuze soft ve animasyonlu gelmesini saglayan motion-framer ozelligi layout propsudur.
  • Ve is terseniz transition ozellikleri belirleyip daha dampingli efektlerde verebilrisiniz.

SlideShow Project

  • @popmotion/popcorn’dan aldigimiz wrap method 1 tane min sayi, 1 tane max sayi ve guncel sayi argumentlerini alir. Ve guncel sayi max sayiyi gecerse, guncel sayi — max sayi arasidnaki farki doner. Farkida gecerse farki — max sayi farkini doner. Bu bizim sliderimizda son renge geldikten sonra en basa donmemizi saglayacak fonksiyonalityi olusturacak.
  • State [[page, direction], setPage] seklinde tanimlamamizin sebebi, page aslinda hangi resim oldugudur, direction ise sagdaki resmi gormek icin resmi sola surukledigimizde veya sag ok buttonuna tiklayip resmi degistirdigimizde direction +1 olacakdir cunku 1 sonraki resmi istiyoruz, digger turlusude -1 olacakdir cunku bir onceki resmi slide I istiyoruz.
  • Tanimladigimiz paginate functionuylada butona tikladigimizda veya drag yaparkenki x konumuna gore page ve direction set ediyoruz.

custom

  • Custom ile default olarak variantslara props gonderebiliriz. AnimatePresence ve motion’a verdigimiz sayesinde bunlari variantsda asagida gordugunuz gibi yakalayabilir ve bunlara gore dynamic degerler verebiliriz.
  • AnimatePresence ve motion.div e ayni anda custom vermemizin sebebi motion child component oldugu icin ilk seferinde customdan gonderdigimiz directionu dogru yakalayamiyor bu yuzden parentinada veriyoruz ki motion duzgun bir sekilde ilk seferindede yakalayabilsin.
  • Burda godnerdigimiz direction ya 1 yada -1. Yukarida anlatmistim zaten. Enter olunca resim soldan girecegi sekilde bir animasyon veriyoruz, ve surukledigimiz resimde exit olunca onun icinde bir baska animasyon ayarliyoruz ve bu ikisinin beraber calismasiyla cok guzel bir slide efekti yakaliyoruz. Mutlaka bu kismi deneyerek gorun.

dragElastic

  • dragElastic suruklenen itemlerin daha hizli ve rahat suruklenmesini saglar normalde biraz yavas ve zor suruklenirler.

Layout

  • Layout ozelligi verdigimiz elementin otomatik animasyon kazanmasini saglar. Diyelim bir butona tikladigimizda kutu sayfanin en ustunden en altina gidecek. Kutu div oldugunu var sayalim. Normalde tikladigimizda en ustten en alta birden animasyonsuz keskin bir sekilde gecer. Fakat div’I motion.div’e cevirdikten sonra layout props unu verirsek eger artik kutu yaptigi o islemi framer-motion un default animation ozellikleriyle yapar. Isterseniz tabi ayarlarini control edebilrisiniz.
  • Asagida header adinda bir elementimiz var. y nin scroll degerine gore animate calistiriyor.
  • Icindeki h1 ise layout propsu almis. Motion.header y nin scroll position 20 den buyukse yani 20px asagi scroll edilmisse justify-contenti center yaparak h1 I ortaliyor. Framer-motion layout gorunce anliyor ve burada devreye girerek soldan ortaya gecen yolda h1’e animasyon vererek center yaptiriyor.

Layout Scale Correction

  • Asagidaki kod acilir kapanir bir accordiondur asagidaki resimdeki gibi.

Kapalı hali

Açık hali

  • Fakat acilisken boyle scale edilme sorunlari ile karsilasabiliyoruz bazen.

  • Bu sorunu cozmek icin tek yapmamiz gereken asagidaki gibi h4 u motion.h4 yapip layout propsu vermektir. Ve bu sorunu cozmus olacagiz.

AnimateSharedLayout & layoutId

  • AnimatedSharedLayout icine layoutId lerle elementlerimizi birbirleriyle iliskilendirebiliriz.
  • Ayrica layout kullandiginiz yerde layoutId kullanacaksaniz layout u silip sadece layoutId de kullanabilirsiniz.
  • 2 tane farklı elemente aynı layoutId lerı vererek bırbırlerıyle baglantılı olmasını isteriz. Mesela asagıda Level Up Tutorıals yazıyor loading ekranında. Bu aslında koddakı motion.h3. Ve ayni zamanda Header kisminda motion.h1 ile yine Level Up Tutorials yazan bir element tanimlamisiz. Bunlarin layoutId leri ayni oldugu icin loading ekrani kapanirken, loading ekranindaki h3, headerdaki h1e animasyonlu bir sekilde kayarak donusur asagidaki gibi.