Kurs Vue.js · Moduł 13: Przejścia i animacje
Named Transitions i tryby przejść
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. Co powoduje dodanie atrybutu name='slide' do <Transition>?
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.