Skip to content

Wszystko o WordPressie, tworzeniu stron — i nie tylko

✨ Animacja linii i zamkniętych konturów SVG na czystym CSS

✨ Animacja linii i zamkniętych konturów SVG na czystym CSS

Animacja obrysu SVG to jeden z tych trików, które zamieniają statyczną ikonę w żywą ilustrację. Na pewno widzieli już Państwo ten efekt: kontur logo płynnie się rysuje, jakby niewidzialne pióro kreśliło kształt od początku do końca. Wygląda kosztownie, a robi się go na czystym CSS.

Problem w tym, że większość poradników zatrzymuje się na najprostszym przypadku: otwarta ścieżka, jedna linia, animacja od początku do końca. Ale prawdziwa grafika to zamknięte kontury, łamane kształty i kierunki rysowania, których projektant nie kontrolował. Ścieżka SVG może zaczynać się gdziekolwiek, a Państwo potrzebują, żeby rysowała się zgodnie z ruchem wskazówek zegara i z estetycznego punktu startowego.

Poniżej pełny zestaw technik: od podstawowej animacji po podwójne linie i pełną kontrolę nad punktem początkowym i końcowym. Wszystko na czystym CSS, bez bibliotek i pomiarów w runtime. Metody sprawdzone podczas prac nad wtyczką SVG Divider i działają w każdej nowoczesnej przeglądarce.

💡 Szybki przegląd:

  • Proszę poznać długość ścieżki SVG przez getTotalLength() lub Illustrator i wpisać liczbę w CSS.
  • Proszę ustawić stroke-dasharray: LENGTH i animować stroke-dashoffset od LENGTH do 0.
  • Aby odwrócić kierunek, proszę zamienić miejscami kreskę i odstęp w stroke-dasharray i przesunąć stroke-dashoffset do przodu.
  • Proszę przesunąć punkt początkowy, dodając do stroke-dashoffset połowę (lub dowolny ułamek) długości.
  • Proszę uruchomić animację „od środka na zewnątrz": start od HALF_LENGTH, koniec na FULL_LENGTH.
  • Proszę skonfigurować podwójne linie przez połówkowy stroke-dasharray i ćwierć-przesunięcie stroke-dashoffset.

Baza techniczna: kreski, odstępy i długość ścieżki

Podstawą każdej animacji SVG są dwie właściwości CSS: stroke-dasharray i stroke-dashoffset. Pierwsza definiuje wzór „kreska-odstęp" wzdłuż konturu, druga przesuwa ten wzór. Jeśli zrobi się kreskę równą pełnej długości ścieżki, a potem zaanimuje przesunięcie, linia „narysuje się" na oczach.

Kluczowy krok przed jakąkolwiek animacją: poznać pełną długość ścieżki. Sposoby:

  • JavaScript: document.querySelector('path').getTotalLength(), jedno wywołanie w konsoli, liczba jest wpisywana w CSS na sztywno. Żadnego runtime.
  • Illustrator: panel „Informacje o dokumencie" pokazuje długość zaznaczonej ścieżki.
  • Własny skrypt: jeśli ścieżek są dziesiątki lub setki, warto napisać automatyczne przejście i oznaczenie długości.

Ważny niuans: getTotalLength() i Illustrator mogą dać nieco różne liczby dla tej samej ścieżki. Przyczyną jest różna precyzja obliczeń. Rozbieżność zwykle mieści się w granicach 1-2 jednostek i nie wpływa na wizualny odbiór.

Proszę sprawdzić geometrię ścieżki pod kątem błędów. Kierunek rysowania i punkt początkowy zależą od tego, jak projektant zbudował krzywą w edytorze, to normalne. Poniżej nauczymy się kontrolować jedno i drugie bez edycji samego SVG.

Podstawowa animacja: rysujemy linię od początku do końca

Najprostszy przypadek: otwarta ścieżka o długości, powiedzmy, 100 jednostek. Ustawiamy kreskę na całą długość i animujemy przesunięcie od 100 do 0:

1@keyframes reveal {
2 to {
3 stroke-dashoffset: 0;
4 }
5}
6svg path {
7 stroke-dashoffset: 100;
8 stroke-dasharray: 100;
9 animation: reveal 1s ease-in-out 0s forwards;
10}

Co tu się dzieje: stroke-dasharray: 100 ustawia 100 jednostek ciągłej linii i 100 jednostek pustki (druga wartość domyślnie równa się pierwszej). stroke-dashoffset: 100 przesuwa wzór tak, że widoczna część wychodzi poza ścieżkę, linia jest niewidoczna. Animacja sprowadza przesunięcie do zera i kreska wsuwa się na miejsce.

Kilka praktycznych uwag:

  • Proszę zastąpić FULL_LENGTH rzeczywistą liczbą bez jednostek miary. Silnik CSS nie rozumie px czy em wewnątrz stroke-dasharray.
  • Selektor svg path jest dla jednego konturu. Jeśli w SVG jest kilka ścieżek, proszę użyć klas, ID lub pseudoklas w rodzaju path:nth-of-type(3). Narzędzia do debugowania: :nth Tester i nthmaster.com.
  • Nazwa @keyframes musi być unikalna dla każdego wariantu animacji. Różne ścieżki często potrzebują własnych klatek kluczowych.
  • Proszę unikać ujemnych wartości stroke-dashoffset: Safari przy ujemnym przesunięciu może migać lub pokazywać artefakty.

Kontrola kierunku: rewers rysowania

Domyślnie animacja idzie od początku ścieżki do końca, tam, gdzie projektant poprowadził krzywą. Ale często potrzebne jest odwrotnie: żeby zamknięty kontur rysował się zgodnie z ruchem wskazówek zegara, a nie przeciwnie.

Rozwiązanie: odwrócić wzór kreski: linia startuje jako niewidoczna (0 kreski, 100 odstępu) i staje się widoczna (100 kreski, 0 odstępu), a przesunięcie ciągnie ją z powrotem:

1@keyframes reversed {
2 from {
3 stroke-dasharray: 0 100;
4 stroke-dashoffset: 0;
5 }
6 to {
7 stroke-dasharray: 100 0;
8 stroke-dashoffset: 100;
9 }
10}
11svg path {
12 animation: reversed 1s ease-in-out 0s forwards;
13}

Mechanika działa dzięki cykliczności wzoru kreski: ciągła linia materializuje się na końcu ścieżki, a jej kopia idzie w lewo za początek i pozostaje niewidoczna. To samo dzieje się na zamkniętych konturach: kopia kreski pojawia się „przed" początkiem i nie przeszkadza w odbiorze.

Istnieje alternatywne podejście, krótsze i bez manipulacji tablicą:

1@keyframes alternative-reversed {
2 from {
3 stroke-dashoffset: 100;
4 }
5 to {
6 stroke-dashoffset: 200;
7 }
8}
9svg path {
10 stroke-dasharray: 100;
11 animation: alternative-reversed 1s ease-in-out 0s forwards;
12}

Tutaj stroke-dasharray jest ustalony na pełną długość, a przesunięcie zmienia się od 100 do 200. Rezultat jest ten sam, linia rysowana jest w odwrotnym kierunku. Proszę użyć wariantu, który jest dla Pana/Pani bardziej intuicyjny.

Przesunięcie punktu początkowego

Zamknięta ścieżka przypomina kolej obwodową: pociąg może wyruszyć z dowolnej stacji. Zadanie polega na przesunięciu początku animacji w estetyczny punkt, bez dotykania samej geometrii.

Start z przeciwnej strony

Dla symetrycznych konturów wystarczy przesunięcie o połowę długości:

1@keyframes start-from-other-side {
2 from {
3 stroke-dasharray: 0 100;
4 stroke-dashoffset: 50;
5 }
6 to {
7 stroke-dasharray: 100 0;
8 stroke-dashoffset: 50;
9 }
10}
11svg path {
12 animation: start-from-other-side 1s ease-in-out 0s forwards;
13}

Kierunek rysowania pozostaje początkowy, ale wizualnie animacja zaczyna się z przeciwległego końca.

Odwrócenie kierunku i dowolny start

Aby jednocześnie odwrócić kierunek i przesunąć punkt startowy, należy dodać pełną długość do końcowego przesunięcia:

1@keyframes other-side-and-reverse {
2 from {
3 stroke-dasharray: 0 100;
4 stroke-dashoffset: 50;
5 }
6 to {
7 stroke-dasharray: 100 0;
8 stroke-dashoffset: 150;
9 }
10}
11svg path {
12 animation: other-side-and-reverse 1s ease-in-out 0s forwards;
13}

Dla całkowicie dowolnego punktu proszę wstawić dowolną wartość zamiast 50. Metodą prób i błędów (plus minus 5-10 jednostek) znajduje się optymalną pozycję. W jednym z projektów punkt startowy na ikonce bitów Twitcha został przesunięty o 17 pikseli, liczba nie jest ładna, ale wizualnie idealna.

Animacja „od środka na zewnątrz"

Linia rośnie od środka w obie strony jednocześnie, to klasyczny zabieg dla logotypów i infografik:

1@keyframes inside-out {
2 from {
3 stroke-dasharray: 0 100;
4 stroke-dashoffset: 50;
5 }
6 to {
7 stroke-dasharray: 100 0;
8 stroke-dashoffset: 100;
9 }
10}
11svg path {
12 animation: inside-out 1s ease-in-out 0s forwards;
13}

Przesunięcie startuje od połowy długości (środek ścieżki) i zmierza do pełnej długości, ciągnąc za sobą kreskę. Dla niesymetrycznych kształtów punkt startu można dodatkowo przesunąć:

1@keyframes custom-inside-out {
2 from {
3 stroke-dasharray: 0 100;
4 stroke-dashoffset: 84;
5 }
6 to {
7 stroke-dasharray: 100 0;
8 stroke-dashoffset: 134;
9 }
10}

Tutaj 84 = HALF_LENGTH + 34. Stała 34 została dobrana ręcznie dla konkretnego kształtu. Nie ma uniwersalnej recepty, proszę próbować, obserwować, korygować.

Animacja „z zewnątrz do środka": podwójne pióro

Podejście przeciwne: linia rysowana jest z dwóch końców jednocześnie, schodząc się do środka. Na otwartych konturach daje to efekt „dwóch piór":

1@keyframes outside-in {
2 from {
3 stroke-dasharray: 0 100;
4 }
5 to {
6 stroke-dasharray: 50 0;
7 }
8}
9svg path {
10 animation: outside-in 1s ease-in-out 0s forwards;
11}

Kreska sięga tylko do połowy długości, przerwa zamyka się dwukrotnie szybciej niż w podstawowej animacji. stroke-dashoffset nie jest tu potrzebny: wzorzec „0 przerwy" w stanie końcowym samoczynnie sprawia, że obie połówki kreski się zwierają.

Pełna kontrola: punkt początkowy i końcowy

Gdy jeden kierunek i punkt startowy to za mało, można sterować obiema granicami rysowania. Dwie ruchome głowice linii zbiegają się we wspólnym punkcie docelowym:

1@keyframes custom-start-end {
2 from {
3 stroke-dasharray: 0 100;
4 stroke-dashoffset: 30;
5 }
6 to {
7 stroke-dasharray: 100 0;
8 stroke-dashoffset: 80;
9 }
10}
11svg path {
12 animation: custom-start-end 1s ease-in-out 0s forwards;
13}

Wartości 30 i 80 są dowolne; przesunięcie from jest mniejsze niż przesunięcie to, to jedyna sztywna reguła. Jeśli punkty są blisko, jeden segment leci szybko, drugi pełznie wolno, to normalne, a nawet efektowne.

Proszę nie bać się wychodzić poza pełną długość w wartości to. stroke-dashoffset: 130 przy długości 100 jest legalne, to po prostu przesuwa cykl kresek na drugie okrążenie. Najważniejsze to unikać wartości ujemnych.

Podwójne linie i efekty specjalne

Najbardziej elastyczny tryb: kreski o połowicznej długości z przesunięciem o ćwiartkę. Każda ścieżka otrzymuje dwie głowice linii poruszające się niezależnie:

1@keyframes double-lines {
2 from {
3 stroke-dasharray: 0 50;
4 stroke-dashoffset: 25;
5 }
6 to {
7 stroke-dasharray: 50 0;
8 stroke-dashoffset: 50;
9 }
10}
11svg path {
12 animation: double-lines 1s ease-in-out 0s forwards;
13}

Dla złożonych kształtów tablica kresek może składać się z kilku komponentów. Suma wszystkich wartości tablicy musi być równa pełnej długości. Rzeczywisty przykład dla znaku nieskończoności:

1@keyframes infinity-double-lines {
2 from {
3 stroke-dasharray: 0 53 0 47;
4 stroke-dashoffset: 37;
5 }
6 to {
7 stroke-dasharray: 53 0 47 0;
8 stroke-dashoffset: 60;
9 }
10}
11svg .infinity {
12 animation: infinity-double-lines 8s ease-in-out 0s alternate infinite;
13 stroke: #8D69D8;
14}

Cztery komponenty tablicy (53 + 47 = 100 = pełna długość) zamieniają się miejscami między from i to. Animacja jest zapętlona (infinite) i odwrócona (alternate), co daje medytacyjny, oddechowy kontur, który nigdy nie powtarza się tak samo.

Wideo: animacja SVG w praktyce

Aby zobaczyć wszystkie opisane techniki na żywo i zrozumieć niuanse konfiguracji, proszę obejrzeć ten anglojęzyczny tutorial:

Autor pokazuje krok po kroku tworzenie animowanej ilustracji SVG od eksportu z Figma po finalne szlify w CSS. Znaczniki czasu w opisie pozwalają przejść od razu do potrzebnego rozdziału.

⁉️🤔 Często zadawane pytania

Po co znać długość ścieżki z góry, skoro jest getTotalLength()?

getTotalLength() to JavaScript, a naszym celem jest czysty CSS bez zależności od runtime’u. Mierzy się długość RAZ (w konsoli lub Illustratorze), wpisuje wartość na sztywno w CSS i animacja działa bez ani jednej linii JS.

Czy można animować kilka ścieżek jedną animacją?

Tak, jeśli mają tę samą długość i pożądane zachowanie. W praktyce ścieżki rzadko się pokrywają, prościej jest zdefiniować dla każdej własny @keyframes lub użyć zmiennych CSS z różnymi wartościami długości.

Dlaczego Safari miga przy ujemnym stroke-dashoffset?

To znany bug renderowania WebKit: ujemne wartości przesunięcia są czasem interpretowane jako natychmiastowy reset do 0. Rozwiązanie: zawsze utrzymywać stroke-dashoffset ≥ 0, a jeśli potrzebne jest przesunięcie, dodać pełną długość zamiast schodzić na minus.

Co zrobić, jeśli animacja „szarpie" na początku lub na końcu?

Proszę dodać forwards do animation (zachowuje stan końcowy) i usunąć alternate, jeśli rewers nie jest potrzebny. Proszę też sprawdzić, czy nie ma konfliktu z innymi regułami CSS dla tego samego elementu, na przykład transition na tej samej właściwości.

Czy działa to z stroke-dasharray na zamkniętych konturach o złożonej geometrii?

Tak, mechanika jest taka sama dla wszystkich ścieżek SVG: prostych, krzywych Béziera, zamkniętych poligonów. Różnica polega tylko na tym, że na zamkniętym konturze nie ma wizualnego „początku" i „końca", dlatego kontrola punktu startowego jest szczególnie ważna dla estetyki.

Czy warto pisać animację ręcznie, skoro są biblioteki?

Krótka odpowiedź: zależy od skali. Dla pojedynczej ikony na landing page’u ręczne sterowanie daje idealny rezultat bez zbędnych zależności. Piętnaście minut na dobranie przesunięcia i kontur rysuje się dokładnie tak, jak Państwo zaplanowali, a nie jak zdecydował twórca biblioteki.

Gdy robiliśmy separatory SVG dla Elementora, ręczne sterowanie zwróciło się potrójnie. Wtyczka wykorzystuje dziesiątki animowanych separatorów, każdy z indywidualnie skonfigurowanymi obrysami. Żadna biblioteka nie dałaby takiej kontroli nad estetyką.

Na techniki z tego artykułu nakładają się dodatkowo:

  • Sterowanie stanami animacji przez JavaScript (start/stop/restart);
  • Leniwy start przez Intersection Observer: kontur rysuje się dopiero, gdy trafi do viewportu;
  • Łańcuchy animacji przez animation-delay;
  • Niestandardowe funkcje wygładzania (cubic-bezier());
  • Dodatkowa praca z przezroczystością w @keyframes;
  • Elastyczna animacja linii przerywanych („ścieżka mrówki").

Proszę wziąć za podstawę szablon z artykułu, podstawić własne wartości długości i eksperymentować z cubic-bezier(). Odpowiednio dobrana funkcja wygładzania zmienia odbiór animacji silniej niż przesunięcie punktu startowego o kilka pikseli.