4/12/2022 ∙ 5 minut czytania

banner

Podstawowe animacje 🎬

Pewnie nie jest to najlepszy moment na implementację animacji w kodzie tej strony. Jest dużo ważniejszych rzeczy do zrobienia, ale bloga prowadzę przede wszystkim dla przyjemności i uznałem, ze przesunę w czasie ważniejsze kroki i dodam do swojej strony drobne animacje.

Animacje w tailwind

W kodzie strony została juz zaimplementowana bardzo podstawowa obsługa trybu ciemnego. Więcej informacji na ten temat można znaleźć w tym artykule. Tutaj zajmę sie drobna aktualizacja sposobu w jaki można te elementy animować:

app/root.tsx:

<body className="bg-white transition duration-300 dark:bg-slate-800">
  <Outlet />
  <ScrollRestoration />
  <Scripts />
  <LiveReload />
</body>

Powyższy kod odpowiada za zdefiniowanie podstawowej tranzycji na każdej stronie. Właściwości na których będzie działać tranzycja pokażą sie po najechaniu na klasę transition i wygląda to następująco:

.transition {
  transition-property: color, background-color, border-color,
    text-decoration-color, fill, stroke, opacity, box-shadow, transform, filter,
    backdrop-filter;
  transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1);
  transition-duration: 150ms;
}

jak widać animujemy wszystkie zmiany kolorów. Domyślnie długość tranzycji trwa 150ms, a dzięki klasie .duration-300 nadpisujemy ją na 300ms:

.duration-300 {
  transition-duration: 300ms;
}

Powyższe dodanie jedynie dwóch klas, czyli transition oraz .duration-300 ustawia podstawowe parametry tranzycji, które aplikują sie na wszystkie kolejne podstrony. Zmiana z trybu ciemnego na jasny i odwrotnie, z poziomu ustawień systemowych systemu operacyjnego na którym pracujemy, spowoduje, ze zobaczymy te animacje.

Animacje framer-motion

Framer-motion to biblioteka, dzięki której możliwe jest deklaratywne pisanie animacji za pomocą komponentów, a właściwie proksów, które biblioteka udostępnia programiście. Cala warstwa abstrakcji utrudniających prace z animacjami jest przed nami ukryta za fasada bardzo prostego interfejsu. Instalacja paczki przebiega nad wyraz standardowo:

npm i framer-motion

Użycie paczki:

Paczka jest użyta póki co w dwóch miejscach. Na stronie głównej oraz na stronie artykułu.

Po wejściu na stronę główna można zauważyć animacje pojawiającego sie tytułu, a pod nia listy ostatnich artykułów. Całość ma fajny efekt, bo kolejne elementy listy pojawiają sie jeden pod drugim w formie wjeżdżających z lewej strony elementów.

Odpowiada za to bardzo prosty kod, który postaram sie tutaj wyjaśnić. Ważna uwaga - pomijam tutaj wszystkie elementy kodu, które nie sa bezpośrednio powiązane z paczka framer-motion.

Krok pierwszy - importy

import { Variants, motion } from 'framer-motion'

Krok drugi - animacja elementu h1 z napisem "Blog"

Weźmy pod lupe pierwszy, bardzo prosty element, czyli header z napisem "Blog":

const header: Variants = {
  hidden: { opacity: 0 },
  visible: {
    opacity: 1,
    transition: {
      duration: 0.5,
    },
  },
}

;<motion.h1
  // ...
  variants={header}
  initial="hidden"
  animate="visible"
>
  Blog
</motion.h1>

Header posiada dwa stany (ukryty i widoczny). Element motion.h1 posiada dodatkowe props'y, które ułatwiają konfiguracje elementu:

Możemy doprecyzować ile czasu ma trwać tranzycja, w jaki sposób powinna wyglądać charakterystyka animacji i za to odpowiada konfiguracja pod zmienna header. Dzięki deklaracji typów Variant działa automatyczne podpowiadanie, które znacząco ułatwia prace z kodem.

Krok drugi - animacje elementów listy

Za ciekawy efekt opóźnienia animacji kolejnych elementów listy odpowiada odpowiednia konfiguracja zmiennej list.

const list: Variants = {
  visible: {
    transition: {
      staggerChildren: 0.03,
      delayChildren: 0.2,
    },
  },
}

;<motion.ul
  //...
  variants={list}
  initial="hidden"
  animate="visible"
>
  {/* ... */}
</motion.ul>

jak widać, interesuje nas tylko przejście do tranzycji visible, która zostanie wykonana po wyrenderowaniu elementu motion.li. W tym momencie zostanie wykonana animacja dla każdego elementu listy. Nie ma tutaj definicji tego jak dany element listy ma sie zachować. Definiujemy tutaj jedynie opóźnienie dla kolejnych elementów listy.

Każdy element listy ma swoja własną animacje, którą wspólny rodzic każdego z nich (motion.ul) będzie orkiestrował. W tym przypadku będzie to odpowiednie opóźnienie w czasie. Dzięki czemu nie musimy juz przypisywać wartości do propsow initial oraz animate na każdym elemencie osobno. Za całość odpowiada lista. Jedynym ważnym aspektem jest to, żeby warianty zarówno listy (motion.ul) oraz elementów listy (motion.li) miały takie samo nazewnictwo:

const item: Variants = {
  hidden: { opacity: 0, x: -10 }, // zacznij od opacity 0 i przesunięcia w lewo o 10px
  visible: { opacity: 1, x: 0 }, // skończ na opacity 1 i przesuń w prawo na pozycje 0
}

;<motion.li
  // ...
  variants={item}
>
  {/* ... */}
</motion.li>

Krok trzeci - animacja artykułu pojawiającego się na stronie

Tutaj sprawa działa analogicznie jak w przypadku listy oraz nagłówka. Jednak tym razem charakterystyka animacji wygląda nieco inaczej:

const variants: Variants = {
  // zacznij od opacity 0 i przesunięcia w górę o 20px
  hidden: {
    opacity: 0,
    y: -20,
  },
  // skończ na opacity 1 i przesuń w dół na pozycję 0
  visible: {
    opacity: 1,
    y: 0,
    // animacja ma trwać 0.5s a jej przebieg ma byc określony funkcją typu "easeOut"
    transition: {
      ease: 'easeOut',
      duration: 0.5,
    },
  },
}

;<motion.article
  // ...
  variants={variants}
  initial={'hidden'}
  animate={'visible'}
>
  {/* ... */}
</motion.article>

Więcej o przebiegach animacji można poczytać tutaj.

> exit

Poniżej znajdziesz więcej postów, które mogą Cię zainteresować.