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

Named Transitions i tryby przejść

5 min czytania
W tej lekcji4

W NOVA LAB mamy wiele paneli - panel diagnostyczny reaktora wsuwa się z boku, panel alertów pojawia się z efektem fade, a panel nawigacji zjeżdża z góry. Vue pozwala nazywać Transition, co zmienia prefiksy klas CSS i umożliwia definiowanie wielu różnych animacji w jednej aplikacji.

W poprzedniej lekcji każda animacja korzystała z klas z prefiksem v-. To działa, dopóki animacja jest jedna. Gdy dodasz drugą, reguły dla v-enter-from i v-leave-to zaczną obowiązywać oba panele naraz, więc reaktor zamiast wsuwać się z boku zacznie przenikać jak alert. Drugi kłopot pojawia się przy podmianie paneli: stary jeszcze wychodzi, a nowy już wchodzi, więc przez chwilę oba zajmują miejsce i układ skacze. W tej lekcji usuniesz obie usterki Sali Holograficznej za pomocą dwóch atrybutów.

Named Transitions

Domyślnie <Transition> używa prefiksu v- dla klas CSS. Atrybut name zmienia ten prefiks na wybraną nazwę:

1<Transition name="slide">
2  <div v-if="visible">...</div>
3</Transition>

Teraz klasy to: slide-enter-from, slide-enter-active, slide-enter-to, slide-leave-from, slide-leave-active, slide-leave-to. Nazwa sama nie dodaje żadnego efektu - zmienia tylko nazwy klas, a wygląd animacji nadal opisujesz w CSS:

1.slide-enter-active,
2.slide-leave-active {
3  transition: all 0.3s ease;
4}
5.slide-enter-from {
6  opacity: 0;
7  transform: translateX(30px);
8}
9.slide-leave-to {
10  opacity: 0;
11  transform: translateX(-30px);
12}

Klasy z końcówką -active określają, jak długo i z jaką krzywą trwa przejście, a enter-from i leave-to mówią, skąd element przylatuje i dokąd odlatuje. Ten panel wjeżdża z prawej, a wyjeżdża w lewo.

Dzięki named transitions możesz mieć jednocześnie wiele typów animacji:

1<template>
2  <!-- Panel reaktora - wsuwa się z prawej -->
3  <Transition name="slide-right">
4    <div v-if="showReactor">Reactor Panel</div>
5  </Transition>
6
7  <!-- Alert - pojawia się z efektem fade -->
8  <Transition name="fade">
9    <div v-if="showAlert">Alert!</div>
10  </Transition>
11
12  <!-- Menu - zjeżdża z góry -->
13  <Transition name="slide-down">
14    <nav v-if="showMenu">Navigation</nav>
15  </Transition>
16</template>

Każdy <Transition> szuka wyłącznie klas ze swoim prefiksem, więc style trzech paneli nie wchodzą sobie w drogę:

1/* Fade */
2.fade-enter-active, .fade-leave-active { transition: opacity 0.3s; }
3.fade-enter-from, .fade-leave-to { opacity: 0; }
4
5/* Slide z prawej */
6.slide-right-enter-active, .slide-right-leave-active { transition: all 0.4s ease; }
7.slide-right-enter-from { transform: translateX(100%); opacity: 0; }
8.slide-right-leave-to { transform: translateX(100%); opacity: 0; }
9
10/* Slide z góry */
11.slide-down-enter-active, .slide-down-leave-active { transition: all 0.3s ease; }
12.slide-down-enter-from { transform: translateY(-100%); opacity: 0; }
13.slide-down-leave-to { transform: translateY(-100%); opacity: 0; }

Zwróć uwagę, że szablon różni się tylko wartością atrybutu name - cała różnica między wsuwaniem, przenikaniem i zjeżdżaniem siedzi w arkuszu stylów.

Tryby przejść (mode)

Gdy przełączasz się między dwoma elementami w jednym <Transition>, domyślnie oba animują się jednocześnie - stary wychodzi, a nowy wchodzi w tym samym czasie. To często wygląda chaotycznie. Atrybut mode kontroluje kolejność:

  • mode="out-in" - stary element wychodzi i dopiero po zakończeniu jego animacji nowy wchodzi. To najczęściej używany tryb - zapobiega "skakaniu" layoutu.
  • mode="in-out" - nowy element wchodzi, a stary wychodzi dopiero potem. Rzadko używany, ale przydatny np. do efektu nakładania się.
  • Brak mode - oba elementy animują się jednocześnie. Może powodować problemy z layoutem.

Tak wygląda przełącznik widoków centrum dowodzenia z trybem out-in:

1<template>
2  <div class="view-switcher">
3    <button @click="currentView = currentView === 'status' ? 'alerts' : 'status'">
4      Switch View
5    </button>
6
7    <Transition name="fade" mode="out-in">
8      <div v-if="currentView === 'status'" key="status" class="panel">
9        System Status: All nominal
10      </div>
11      <div v-else key="alerts" class="panel">
12        Active Alerts: 3
13      </div>
14    </Transition>
15  </div>
16</template>
17
18<script setup>
19import { ref } from 'vue'
20
21const currentView = ref('status')
22</script>

Po kliknięciu panel statusu najpierw całkowicie znika, a dopiero potem pojawia się panel alertów, więc w żadnym momencie nie ma na ekranie dwóch paneli naraz.

Ważne: W Vue 3 gałęzie v-if i v-else dostają unikalne klucze automatycznie, więc przełączanie dwóch <div> zadziała także bez atrybutu key - jawny key, jak w przykładzie, nie szkodzi i dobrze opisuje intencję. Klucz staje się niezbędny wtedy, gdy ten sam element zostaje na miejscu, a zmienia się tylko jego treść, na przykład <span :key="count">{{ count }}</span>. Bez :key Vue podmieniłoby sam tekst w istniejącym elemencie i przejście w ogóle by się nie uruchomiło.

Przełączanie komponentów

mode="out-in" świetnie działa z dynamicznymi komponentami. Element <component :is> renderuje komponent wskazany w zmiennej, a shallowRef przechowuje go bez głębokiej reaktywności, bo liczy się tylko podmiana całego komponentu:

1<template>
2  <Transition name="slide" mode="out-in">
3    <component :is="currentTab" />
4  </Transition>
5</template>
6
7<script setup>
8import { shallowRef } from 'vue'
9import StatusPanel from './StatusPanel.vue'
10import AlertsPanel from './AlertsPanel.vue'
11
12const currentTab = shallowRef(StatusPanel)
13</script>

Przy każdej zmianie currentTab Vue najpierw odegra wyjście starego panelu, a dopiero potem wejście nowego. StatusPanel i AlertsPanel to dwie różne definicje komponentów, więc animacja rusza bez dodatkowego klucza.

Praktyczna wskazówka

Dla prostych przełączeń między widokami, mode="out-in" z animacją fade to najbardziej eleganckie rozwiązanie. Unikaj mode="in-out" chyba że masz konkretny powód (np. nakładający się efekt slide). Brak mode sprawdza się głównie gdy oba elementy mają position: absolute i nie wpływają na layout.

Moja rada: nazywaj przejścia od ruchu, a nie od miejsca użycia - slide-right i fade przydadzą się w dziesięciu panelach, a reactor-animation tylko w jednym. Ta sama para name i mode wróci w lekcji o przejściach między trasami, gdzie owiniesz nią cały <RouterView>.

Zapamiętaj: name nadaje animacji własny zestaw klas, a mode ustala kolejkę - najpierw jeden panel znika, dopiero potem pojawia się drugi.

Kod do tej lekcji: App.vue
1<script setup>
2import { ref } from 'vue'
3
4const currentView = ref('panel')
5const views = ['panel', 'czujniki', 'załoga']
6</script>
7
8<template>
9  <div class="nova-lab">
10    <h1>Named Transitions i tryby przejść</h1>
11
12    <div class="nav">
13      <button
14        v-for="view in views"
15        :key="view"
16        @click="currentView = view"
17        :class="{ active: currentView === view }"
18        class="nav-btn"
19      >
20        {{ view }}
21      </button>
22    </div>
23
24    <!-- Transition z nazwanymi klasami i trybem out-in -->
25    <Transition name="slide" mode="out-in">
26      <div :key="currentView" class="panel">
27        <h2>{{ currentView.toUpperCase() }}</h2>
28        <p v-if="currentView === 'panel'">Panel główny stacji NOVA LAB</p>
29        <p v-else-if="currentView === 'czujniki'">Odczyty czujników atmosferycznych</p>
30        <p v-else>Lista członków załogi</p>
31      </div>
32    </Transition>
33  </div>
34</template>
35
36<style scoped>
37.nova-lab {
38  background: #0a0e27;
39  color: #00ff88;
40  min-height: 100vh;
41  padding: 20px;
42  font-family: monospace;
43}
44h1 { color: #00ff88; }
45
46.nav { display: flex; gap: 10px; margin: 15px 0; }
47.nav-btn {
48  background: rgba(0, 180, 216, 0.2);
49  color: #00b4d8;
50  border: 1px solid #00b4d8;
51  padding: 8px 16px;
52  border-radius: 6px;
53  cursor: pointer;
54}
55.nav-btn.active {
56  background: #00b4d8;
57  color: white;
58}
59
60.panel {
61  background: rgba(0, 255, 136, 0.05);
62  border: 2px solid #00ff88;
63  padding: 20px;
64  border-radius: 12px;
65}
66
67/* klasy slide- (name="slide") */
68.slide-enter-active,
69.slide-leave-active {
70  transition: all 0.3s ease;
71}
72.slide-enter-from {
73  opacity: 0;
74  transform: translateX(30px);
75}
76.slide-leave-to {
77  opacity: 0;
78  transform: translateX(-30px);
79}
80</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 powoduje dodanie atrybutu name='slide' do <Transition>?

  2. 2. Co robi tryb mode='out-in' w <Transition>?

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

Zadania praktyczne w grze

  • Edytor kodu

    Dodaj <Transition name='slide'> i odpowiednie klasy CSS slide-*.

  • Układanie w poziomie

    Ułóż poprawną składnię Transition z trybem out-in:

  • Układanie w pionie

    Ułóż kolejność klas CSS podczas animacji wyjścia elementu:

  • Edytor kodu

    Użyj <Transition mode='out-in'> do płynnego przełączania między komponentami.

Przydatne artykuły