Statki kosmiczne nie pojawiają się nagle na ekranie radaru - wlatują, pulsują sygnałem, płynnie przesuwają się po orbicie. Animacje w interfejsach webowych działają na tej samej zasadzie: nadają aplikacji dynamiki i pomagają użytkownikowi zrozumieć, co się dzieje. W tej lekcji poznasz techniki tworzenia animacji CSS w aplikacjach React - od prostych przejść po złożone sekwencje z keyframes, a na końcu zajrzymy do biblioteki React Transition Group.
Reguła
@keyframes pozwala zdefiniować sekwencję kroków animacji. W styled-components korzystamy z helpera keyframes, który generuje unikalną nazwę animacji i zapobiega konfliktom:1import styled, { keyframes } from 'styled-components';
2
3// Definicja animacji - pulsowanie sygnału radarowego
4const radarPulse = keyframes`
5 0% {
6 transform: scale(1);
7 opacity: 1;
8 }
9 50% {
10 transform: scale(1.5);
11 opacity: 0.5;
12 }
13 100% {
14 transform: scale(2);
15 opacity: 0;
16 }
17`;
18
19const RadarDot = styled.div`
20 width: 20px;
21 height: 20px;
22 border-radius: 50%;
23 background: #00ff88;
24 animation: ${radarPulse} 2s ease-out infinite;
25`;Kluczowe elementy:
keyframes importujemy z styled-components (nie z CSS)0%, 50%, 100%) lub słowa kluczowe (from, to)${radarPulse} w propercie animationMożesz definiować dowolnie wiele keyframes i łączyć je w różnych komponentach:
1const floatInSpace = keyframes`
2 0%, 100% { transform: translateY(0px); }
3 50% { transform: translateY(-15px); }
4`;
5
6const rotateOrbit = keyframes`
7 from { transform: rotate(0deg); }
8 to { transform: rotate(360deg); }
9`;
10
11const SpaceStation = styled.div`
12 animation: ${floatInSpace} 4s ease-in-out infinite;
13`;
14
15const OrbitRing = styled.div`
16 animation: ${rotateOrbit} 10s linear infinite;
17`;Właściwość
transition w CSS to najprostszy sposób na animowanie zmian wartości. Zamiast definiować keyframes, mówisz przeglądarce: "gdy ta właściwość się zmieni, zrób to płynnie". To idealne rozwiązanie dla efektów hover, kliknięcia czy zmiany stanu komponentu.1const NavButton = styled.button`
2 background: #1a1a3e;
3 color: #00ff88;
4 padding: 12px 24px;
5 border: 2px solid #00ff88;
6 border-radius: 8px;
7 cursor: pointer;
8
9 /* Definiujemy, co ma być animowane i jak */
10 transition: all 0.3s ease;
11
12 &:hover {
13 background: #00ff88;
14 color: #1a1a3e;
15 transform: scale(1.05);
16 box-shadow: 0 0 20px rgba(0, 255, 136, 0.4);
17 }
18`;Składnia
transition:1transition: property duration timing-function delay;property - co animujemy (all, background, transform, opacity...)duration - jak długo trwa przejście (0.3s, 200ms)timing-function - krzywa animacji (ease, linear, ease-in-out, cubic-bezier())delay - opóźnienie startu (opcjonalne)Możesz animować wiele właściwości z różnymi parametrami:
1const ShipPanel = styled.div`
2 opacity: 0.8;
3 transform: translateX(0);
4 background: #0a0a2e;
5
6 transition:
7 opacity 0.2s ease,
8 transform 0.4s cubic-bezier(0.25, 0.46, 0.45, 0.94),
9 background 0.3s ease;
10
11 &:hover {
12 opacity: 1;
13 transform: translateX(10px);
14 background: #1a1a4e;
15 }
16`;Właściwość
animation to bardziej zaawansowana wersja transition. Pozwala na ciągłe, powtarzające się animacje, odtwarzanie wstecz i kontrolę nad każdym aspektem ruchu:1const warningBlink = keyframes`
2 0%, 100% { opacity: 1; }
3 50% { opacity: 0.3; }
4`;
5
6const WarningLight = styled.div`
7 width: 16px;
8 height: 16px;
9 border-radius: 50%;
10 background: #ff4444;
11
12 animation-name: ${warningBlink};
13 animation-duration: 1s;
14 animation-timing-function: ease-in-out;
15 animation-iteration-count: infinite;
16 animation-direction: alternate;
17`;Albo w skróconej formie:
1animation: name duration timing-function delay iteration-count direction fill-mode;Najważniejsze parametry:
iteration-count - infinite (nieskończone) lub liczba powtórzeń (3)direction - normal, reverse, alternate (tam i z powrotem)fill-mode - forwards (zachowaj stan końcowy), backwards, bothplay-state - running lub paused (kontrola odtwarzania)W styled-components możesz tworzyć animacje, które reagują na props komponentu. To pozwala na dynamiczne dostosowanie ruchu do stanu aplikacji:
1const moveShip = keyframes`
2 from { transform: translateX(0); }
3 to { transform: translateX(100%); }
4`;
5
6const Spaceship = styled.div`
7 width: 60px;
8 height: 30px;
9 background: #4488ff;
10 clip-path: polygon(0% 50%, 30% 0%, 100% 50%, 30% 100%);
11
12 animation: ${moveShip} ${props => props.speed || 3}s linear infinite;
13 animation-play-state: ${props => props.isFlying ? 'running' : 'paused'};
14 opacity: ${props => props.isFlying ? 1 : 0.5};
15 transition: opacity 0.3s ease;
16`;
17
18function FlightControl() {
19 const [isFlying, setIsFlying] = useState(false);
20 const [speed, setSpeed] = useState(3);
21
22 return (
23 <div>
24 <Spaceship isFlying={isFlying} speed={speed} />
25
26 <button onClick={() => setIsFlying(!isFlying)}>
27 {isFlying ? 'Zatrzymaj' : 'Leć'}
28 </button>
29
30 <input
31 type="range"
32 min="0.5"
33 max="5"
34 step="0.5"
35 value={speed}
36 onChange={(e) => setSpeed(e.target.value)}
37 />
38 <span>Prędkość: {speed}s na okrążenie</span>
39 </div>
40 );
41}Możesz też dynamicznie wybierać różne keyframes w zależności od stanu:
1const slideIn = keyframes`
2 from { transform: translateX(-100%); opacity: 0; }
3 to { transform: translateX(0); opacity: 1; }
4`;
5
6const slideOut = keyframes`
7 from { transform: translateX(0); opacity: 1; }
8 to { transform: translateX(100%); opacity: 0; }
9`;
10
11const Panel = styled.div`
12 animation: ${props => props.isVisible ? slideIn : slideOut} 0.4s ease forwards;
13`;To podejście pozwala na animowanie wejścia i wyjścia elementów - panel wsuwa się z lewej strony przy pojawieniu i wysuwa w prawo przy znikaniu.
Natywne CSS nie potrafi animować elementów, które są dodawane lub usuwane z DOM (np. przez warunkowe renderowanie
{show && <Component />}). Biblioteka React Transition Group rozwiązuje ten problem, zarządzając cyklem życia animacji wejścia/wyjścia.Najczęściej używany komponent to
CSSTransition:1import { CSSTransition } from 'react-transition-group';
2import { useState, useRef } from 'react';
3
4function NotificationPanel() {
5 const [showAlert, setShowAlert] = useState(false);
6 const nodeRef = useRef(null);
7
8 return (
9 <div>
10 <button onClick={() => setShowAlert(!showAlert)}>
11 {showAlert ? 'Ukryj alert' : 'Pokaż alert'}
12 </button>
13
14 <CSSTransition
15 in={showAlert}
16 timeout={300}
17 classNames="alert"
18 unmountOnExit
19 nodeRef={nodeRef}
20 >
21 <div ref={nodeRef} className="alert-box">
22 Wykryto obiekt na radarze!
23 </div>
24 </CSSTransition>
25 </div>
26 );
27}CSSTransition automatycznie dodaje i usuwa klasy CSS w odpowiednich momentach:1/* Wejście - start */
2.alert-enter {
3 opacity: 0;
4 transform: translateY(-20px);
5}
6
7/* Wejście - aktywna faza */
8.alert-enter-active {
9 opacity: 1;
10 transform: translateY(0);
11 transition: opacity 300ms ease, transform 300ms ease;
12}
13
14/* Wyjście - start */
15.alert-exit {
16 opacity: 1;
17 transform: translateY(0);
18}
19
20/* Wyjście - aktywna faza */
21.alert-exit-active {
22 opacity: 0;
23 transform: translateY(-20px);
24 transition: opacity 300ms ease, transform 300ms ease;
25}Kluczowe propsy
CSSTransition:in - boolean kontrolujący, czy element jest widocznytimeout - czas trwania animacji (w ms), powinien odpowiadać CSS transitionclassNames - prefiks klas CSS (np. "alert" generuje alert-enter, alert-enter-active itd.)unmountOnExit - usuwa element z DOM po animacji wyjścianodeRef - ref do animowanego elementu (zalecane dla uniknięcia ostrzeżeń)Podsumujmy, kiedy używać poszczególnych technik:
- dla prostych zmian stanów:transition
+ @keyframes
- dla złożonych i ciągłych animacji:animation
React Transition Group - dla animacji cyklu życia komponentów:
Dobrze zaprojektowane animacje to jak system nawigacyjny statku - prowadzą użytkownika, dają feedback i sprawiają, że interfejs czuje się naturalnie, jak lot przez galaktykę z warp drive, a nie teleportacja bez ostrzeżenia.