Kurs Vue.js · Moduł 13: Przejścia i animacje

Zaawansowane wzorce animacji

4 min czytania
W tej lekcji5

W zaawansowanych systemach NOVA LAB potrzebujemy wyrafinowanych efektów - kaskadowe ładowanie elementów dashboardu, animacja przy pierwszym wyświetleniu strony, precyzyjna kontrola czasów trwania i szacunek dla preferencji użytkowników dotyczących ruchu. Poznaj wzorce, które nadają interfejsom profesjonalny charakter.

Appear - animacja przy mount

Domyślnie <Transition> animuje element tylko przy przełączeniu v-if/v-show. Atrybut appear powoduje, że animacja uruchomi się również przy pierwszym renderowaniu (mount) komponentu:

1<Transition appear name="fade">
2  <div>Ten element animuje się przy załadowaniu strony</div>
3</Transition>

To idealne rozwiązanie dla elementów widocznych od razu po załadowaniu - logo stacji, nagłówek dashboardu, panel statusu. Bez appear te elementy po prostu "byłyby tam" bez żadnej animacji wejścia.

Możesz użyć specjalnych klas CSS dla animacji appear (niezależnych od normalnych klas enter):

1<Transition
2  appear
3  appear-from-class="appear-start"
4  appear-active-class="appear-running"
5  appear-to-class="appear-end"
6>
7  <div>Custom appear animation</div>
8</Transition>

Lub z bibliotekami:

1<Transition
2  appear
3  appear-active-class="animate__animated animate__fadeInDown"
4>
5  <header>NOVA LAB Control Center</header>
6</Transition>

Staggered list animation

Jeden z najbardziej efektownych wzorców - kaskadowe pojawianie się elementów listy, gdzie każdy kolejny element pojawia się z rosnącym opóźnieniem. Wygląda jak wyświetlanie danych na holograficznym dashboardzie NOVA LAB, element po elemencie:

1<TransitionGroup
2  appear
3  :css="false"
4  @before-enter="onBeforeEnter"
5  @enter="onEnter"
6  tag="div"
7>
8  <div v-for="(item, index) in items" :key="item.id" :data-index="index">
9    {{ item.name }}
10  </div>
11</TransitionGroup>

Kluczem jest atrybut :data-index - przekazuje indeks elementu do hooka JS, który oblicza opóźnienie:

1function onBeforeEnter(el) {
2  el.style.opacity = 0
3  el.style.transform = 'translateY(20px)'
4}
5
6function onEnter(el, done) {
7  // Każdy kolejny element ma o 150ms większe opóźnienie
8  const delay = el.dataset.index * 150
9
10  setTimeout(() => {
11    el.style.transition = 'all 0.5s ease'
12    el.style.opacity = 1
13    el.style.transform = 'translateY(0)'
14    el.addEventListener('transitionend', done, { once: true })
15  }, delay)
16}

Efekt: element 0 pojawia się natychmiast, element 1 po 150ms, element 2 po 300ms itd. Dla 10 elementów cała animacja trwa 1.5s + 0.5s (czas samej animacji) = ~2s.

Wskazówka: Ogranicz opóźnienie do max ~1s, aby użytkownik nie czekał zbyt długo na ostatnie elementy:

1const delay = Math.min(el.dataset.index * 100, 1000)

Explicit duration

Gdy element ma zarówno CSS transition jak i animation, lub gdy zagnieżdżone elementy mają różne czasy animacji, Vue może nie wykryć prawidłowego momentu zakończenia. Atrybut :duration pozwala jawnie określić czas trwania w milisekundach:

1<!-- Jednolity czas dla wejścia i wyjścia -->
2<Transition :duration="500">...</Transition>
3
4<!-- Różne czasy dla wejścia i wyjścia -->
5<Transition :duration="{ enter: 300, leave: 500 }">...</Transition>

Jest to szczególnie przydatne gdy:

  • Animujesz zagnieżdżone elementy z różnymi czasami
  • Łączysz CSS transition i animation na tym samym elemencie
  • Używasz bibliotek zewnętrznych, których czas trwania nie jest automatycznie wykrywany

Accessibility - prefers-reduced-motion

Szanuj ustawienia użytkownika dotyczące ruchu. Osoby z zaburzeniami przedsionkowymi lub epilepsją mogą mieć włączoną preferencję prefers-reduced-motion. Twój CSS powinien to respektować:

1/* Normalne animacje */
2.fade-enter-active,
3.fade-leave-active {
4  transition: opacity 0.3s ease, transform 0.3s ease;
5}
6
7.fade-enter-from,
8.fade-leave-to {
9  opacity: 0;
10  transform: translateY(10px);
11}
12
13/* Wyłącz animacje dla użytkowników, którzy ich nie chcą */
14@media (prefers-reduced-motion: reduce) {
15  .fade-enter-active,
16  .fade-leave-active {
17    transition: none;
18  }
19
20  .fade-enter-from,
21  .fade-leave-to {
22    opacity: 1;
23    transform: none;
24  }
25}

Możesz też obsłużyć to w JavaScript:

1const prefersReducedMotion = window.matchMedia('(prefers-reduced-motion: reduce)').matches
2
3function onEnter(el, done) {
4  if (prefersReducedMotion) {
5    done() // Pomiń animację
6    return
7  }
8  // Normalna animacja...
9}

Best practices wydajności

  1. Animuj tylko transform i opacity - te właściwości są akcelerowane przez GPU i nie powodują reflow/repaint. Unikaj animowania width, height, top, left, margin, padding.

  2. Używaj will-change dla ciężkich animacji - informuje przeglądarkę o nadchodzącej animacji, pozwalając na optymalizację:

1.heavy-animation-enter-active {
2  will-change: transform, opacity;
3  transition: all 0.5s ease;
4}
  1. Preferuj CSS nad JS - CSS animations są optymalizowane przez przeglądarkę i działają na osobnym wątku. Używaj JS hooków tylko gdy potrzebujesz dynamicznych wartości.

  2. Krótkie czasy trwania - animacje UI powinny trwać 200-500ms. Dłuższe animacje spowalniają interakcję i irytują użytkowników.

  3. Testuj na urządzeniach mobilnych - animacje, które działają płynnie na desktopie, mogą lagować na starszych telefonach.

Kod do tej lekcji: App.vue
1<script setup>
2import { ref } from 'vue'
3
4// Appear - animacja przy montowaniu
5const items = ref([
6  { id: 1, name: 'Podtrzymanie życia', delay: 0 },
7  { id: 2, name: 'Nawigacja', delay: 100 },
8  { id: 3, name: 'Komunikacja', delay: 200 },
9  { id: 4, name: 'Sieć energetyczna', delay: 300 },
10  { id: 5, name: 'Laboratorium naukowe', delay: 400 }
11])
12
13function onBeforeEnter(el) {
14  el.style.opacity = 0
15  el.style.transform = 'translateY(20px)'
16}
17
18function onEnter(el, done) {
19  const delay = el.dataset.index * 150
20  setTimeout(() => {
21    el.style.transition = 'all 0.5s ease'
22    el.style.opacity = 1
23    el.style.transform = 'translateY(0)'
24    el.addEventListener('transitionend', done, { once: true })
25  }, delay)
26}
27</script>
28
29<template>
30  <div class="nova-lab">
31    <h1>Lista kaskadowa + appear</h1>
32
33    <TransitionGroup
34      appear
35      :css="false"
36      @before-enter="onBeforeEnter"
37      @enter="onEnter"
38      tag="div"
39      class="systems"
40    >
41      <div
42        v-for="(item, index) in items"
43        :key="item.id"
44        :data-index="index"
45        class="system-card"
46      >
47        <span class="dot">●</span>
48        {{ item.name }}
49      </div>
50    </TransitionGroup>
51  </div>
52</template>
53
54<style scoped>
55.nova-lab {
56  background: #0a0e27;
57  color: #00ff88;
58  min-height: 100vh;
59  padding: 20px;
60  font-family: monospace;
61}
62h1 { color: #00ff88; }
63.systems { margin-top: 20px; }
64.system-card {
65  display: flex;
66  align-items: center;
67  gap: 12px;
68  background: rgba(0, 180, 216, 0.1);
69  border: 1px solid #00b4d8;
70  padding: 14px 20px;
71  margin: 8px 0;
72  border-radius: 8px;
73  font-size: 14px;
74}
75.dot { color: #00ff88; }
76</style>

Widzisz błąd w tej lekcji?

Sprawdź się

Odpowiedz na pytania z tej lekcji. Wybierz odpowiedź, a od razu zobaczysz, czy jest poprawna.

  1. 1. Co robi atrybut appear w <Transition>?

  2. 2. Jak ustawić jawny czas trwania animacji w <Transition>?

To 2 z 4 pytań do tej lekcji. Pozostałe rozwiążesz w grze.

Zadania praktyczne w grze

  • Układanie w pionie

    Ułóż kroki implementacji staggered (opóźnionej) animacji listy:

  • Edytor kodu

    Użyj data-index i gsap/setTimeout do staggered animation w TransitionGroup.

  • Układanie w poziomie

    Ułóż atrybut appear z custom class:

  • Klikanie w kolejności

    Ułóż kod przekazania indeksu do elementu listy:

  • Układanie w pionie

    Ułóż techniki animacji od najprostszej do najbardziej zaawansowanej:

  • Układanie w poziomie

    Ułóż składnię duration z różnymi czasami enter/leave:

  • Klikanie w kolejności

    Ułóż hooki JavaScript w kolejności wywołania (animacja wyjścia):

  • Klikanie w kolejności

    Ułóż deklarację CSS transition shorthand:

  • Układanie w pionie

    Ułóż 6 klas CSS dodawanych przez <Transition> w kolejności chronologicznej:

Przydatne artykuły