Animacje komponentów z Framer Motion 🚀

Animacje komponentów z Framer Motion 🚀

01/04/2023
6 min read
Dzisiaj chciałbym przedstawić Wam moje ulubione narzędzie do animacji w projektach React - Framer Motion! Jest to bardzo prosta i przyjemna do pracy biblioteka do pracy z animacjami
We wpisie tym przyjrzymy się bliżej Framer Motion, omówimy jego najważniejsze funkcje i nauczymy się, jak dodać piorunujące efekty do naszych aplikacji. Aby pokazać Wam, jak to wszystko działa, zbudujemy razem prosty, ale efektowny komponent Drawer, który doskonale sprawdzi się w wielu projektach.
A przede wszystkim - przekonamy się, że tworzenie animacji w React może być naprawdę proste i przyjemne. Do dzieła! 🎉

Animacje, interakcje i więcej: Poznaj Framer Motion

Framer Motion oferuje szeroki zakres możliwości, które pozwalają na tworzenie różnorodnych animacji i interakcji w aplikacjach React. Oto niektóre z nich:
  1. Animacje CSS: Umożliwia animowanie różnych właściwości CSS, takich jak kolor, wielkość, marginesy czy pozycję elementów.
  2. Drag and drop: Prosta implementacja mechanizmu "przeciągnij i upuść" dla elementów na stronie.
  3. Gesty: Obsługa gestów, takich jak przesunięcia czy przybliżania, co pozwala na tworzenie bardziej interaktywnych aplikacji.
  4. Spring physics: Zastosowanie fizyki sprężynowej do animacji, co pozwala na tworzenie bardziej realistycznych i płynnych efektów.
  5. Orchestration: Kontrolowanie kolejności i synchronizacji animacji wielu elementów, dzięki czemu możemy tworzyć złożone sceny.
  6. Variants: Tworzenie zdefiniowanych wariantów animacji dla różnych stanów, co ułatwia zarządzanie i kontrolowanie animacji.
  7. Shared layout animations: Ułatwienie animacji między różnymi komponentami, np. podczas zmiany stron w aplikacji.
  8. AnimatePresence: Łatwe dodawanie animacji wejścia i wyjścia dla komponentów, które pojawiają się i znikają z drzewa DOM.
  9. Server-side rendering (SSR) compatibility: Współpraca z renderowaniem po stronie serwera, co ułatwia tworzenie zoptymalizowanych aplikacji.
  10. Performance optimizations: Optymalizacja wydajności animacji, dzięki czemu działają one płynnie nawet na słabszych urządzeniach.
Dzięki tym funkcjom Framer Motion pozwala na tworzenie atrakcyjnych, płynnych i responsywnych animacji w aplikacjach React. W kolejnych częściach tego wpisu zastosujemy niektóre z tych możliwości, aby stworzyć drawer komponent z animacjami i gestami.

Konfiguracja projektu

Instalujemy bibliotekę standardowo za pomocą npm/yarn:
npm install framer-motion
Następnie tworzymy nowy komponent i piszemy strukturę HTML:
function Drawer() {
const [open, setOpen] = useState(false);
return (
<>
{open && (
<div
className="drawerOverlay"
/>
)}
<div
className='drawer'
>
<div
className='drawerInner'
>
<DrawerHeader />
<DrawerContent />
</div>
</div>
</>
);
}
I stylizujemy trochę za pomocą CSS:
.drawer {
font-family: 'Open Sans', sans-serif;
position: fixed;
top: calc(100% - 94px);
width: 100%;
left: 0;
right: 0;
height: 90%;
z-index: 4;
pointer-events: initial;
}
.drawerInner {
height: 100%;
}
.drawerOverlay {
width: 100%;
height: 100vh;
position: fixed;
background-color: rgba(0, 0, 0, 0.3);
top: 0;
left: 0;
z-index: 3;
backdrop-filter: blur(0.4rem);
pointer-events: initial;
transition: all 0.3s ease-in-out;
}
Mając tak przygotowaną podstawową strukturę, możemy przystąpić do animowania otwierania i zamykania drawera oraz obsługi gestu drag.

Dodanie animacji z framer motion

Zajmijmy się najpierw animacją overlaya. Założenie jest takie, że kiedy nasz komponent będzie otwarty, w tle pokażemy półprzeźroczysty wyblurowany overlay. Będzie on również obsługiwał zamknięcie drawera po kliknięciu na overlay. Element ten renderuje się dopiero, kiedy zmienna open będzie ustawiona na true i tak samo usuwa się z DOM kiedy zamkniemy drawer. Jako że zamknięcie overlaya sprawia, że element znika z DOM, musimy zastosować tutaj <AnimatePresence>.
Jest to komponent dostarczany przez framer motion, który pozwala animować elementy, które znikają z DOM. Nie dalibyśmy rady tego zrobić za pomocą zwykłego CSS-a.
Overlayowi dodamy również drobną animację opacity. Aby to zrobić, musimy przygotować obiekt, który będzie zawierał konfiguracje animacji. W framer motion możemy to zrobić za pomocą Variants:
const overlayVariants: Variants = {
hidden: { opacity: 0 },
visible: { opacity: 1 },
};
Obiekt reprezentuje 2 stany, jakie przyjmie nasz overlay - opacity: 0 i opacity: 1. Dzięki temu uzyskamy efekt płynnego pojawiania się i znikania, zamiast skokowego.
<AnimatePresence>
{open && (
<motion.div
className="drawerOverlay"
initial="hidden"
animate="visible"
exit="hidden"
onClick={handleCloseDrawer}
variants={overlayVariants}
/>
)}
</AnimatePresence>
Całość musimy owrapować wspomnianym wcześniej <AnimatePresence/>. Następnie div zamieniamy na strukturę <motion.div, dzięki czemu otrzymamy funkcjonalności framer motion. Do propsów initial, animate i exit musimy przypisać odpowiednie animacje. Animacje te zostaną pobrane z obiektu overlayVariants, który musimy przekazać do propa variants. Poszczególne propsy odpowiadają za:
  • initial - stan początkowy komponentu
  • animate - jakie własności elementu mają zostać zanimowane - w naszym przypadku opacity ma zostać zanimowane od wartości 0 do wartości 1
  • exit - jaka animacja ma zostać odpalona na usunięcie elementu z DOM. W naszym przypadku będzie to animacja ze stanu visible do hidden
Tyle jest wystarczające, aby otrzymać ładną animację pojawiania się i znikania overlaya.
Przejdźmy teraz do głównej animacji otwierania drawera.
Musimy stworzyć kolejny obiekt typu Variants, tym razem dla animacji otwierania i zamykania drawera:
const variants: Variants = {
inactive: { y: 0 },
active: { y: '-80%' },
};
W Drawerze animowana będzie pozycja. Na początku na ekranie widoczny będzie przycisk do otwarcia drawera i kawałek treści, a po otwarciu zmienimy wartość y komponentu, aby był widoczny na 80% całej strony.
Równocześnie chcemy, aby drawer otwierał i zamykał się na gest pociągnięcia w górę/pociągnięcia w dół. Musimy zatem stworzyć funkcję, która będzie rozpoznawała w jaki sposób użytkownik wykonał gest drag:
const handleDragEnd = (_: unknown, info: PanInfo): void => {
if (info.offset.y < 0 && info.offset.y < -80) {
setOpen(true);
}
if (info.offset.y > 0 && info.offset.y > 80) {
setOpen(false);
}
};
W obiekcie info znajdują się wszystkie potrzebne dane dotyczące wykonanego gestu. Funkcja nazywa się handleDragEnd, ponieważ musimy obsłużyć tylko koniec wykonania gestu. Za pomocą info.offset wiemy dokładnie o jakie wartości drawer został przesunięty. Nie chcemy, aby za każdym gestem drawer się otwierał bądź zamykał, dlatego dodajemy bufor w postaci 80px, czyli jeżeli użytkownik przesunął za pomocą drag drawera o więcej niż 80px, to znaczy, że chce otworzyć bądź zamknąć.
Taka konfiguracja jest wystarczająca, aby móc zaktualizować naszą strukturę HTML:
<motion.div
className={'drawer'}
dragElastic={0.8}
onDragEnd={handleDragEnd}
dragConstraints={{ top: 0, bottom: 0 }}
drag="y"
>
<motion.div
className={'drawerInner'}
transition={{ type: 'spring', duration: 0.8 }}
variants={variants}
initial="inactive"
animate={open ? 'active' : 'inactive'}
>
<DrawerHeader />
<div className='drawer-content'>
{/* Placeholder for content */}
</div>
</motion.div>
</motion.div>
Omówiliśmy wcześniej już propsy typu variants, initial i animate, więc tutaj skupimy się bardziej na obsłudze drag. Jak można zauważyć, odpowiada za to kilka wartości:
  • drag="y" - definiuje nam, w którą stronę użytkownik może przeciągać. U nas tylko w pionie (góra-dół)
  • dragConstraints - definiuje nam granice, za jakie element może zostać przeciagnięty. My blokujemy w taki sposób, aby nie można było wyjść poza górę i dół.
  • onDragEnd - metoda odpalająca się, kiedy użytkownik skończy wykonywać gest - tutaj przekazujemy wcześniej zdefiniowaną funkcję
  • dragElastic i transition - dodatkowe parametry animacji. Zachęcam do zabawy z nimi!

Rezultat końcowy

I to by było na tyle. Jak widać, nie potrzeba dużo skomplikowanego kodu, aby uzyskać ładną, płynną i wydajną animację. Oto prezentuje się efekt końcowy:
FramerMotionAnimationDrawer
Cały projekt dostępny jest na githubie: https://github.com/dawidkostrzewa/drawer-framer-motion

Zobacz więcej wpisów