Jak podejść do animacji SVG w CSS

Opublikowany: 2022-03-11

Animacje są wszechobecną częścią sieci. W przeciwieństwie do migających obrazów GIF, które nękały witryny internetowe w dawnych czasach, dzisiejsze animacje są bardziej subtelne i gustowne. Projektanci i specjaliści od front-endu wykorzystują je, aby strony internetowe wyglądały na bardziej dopracowane, poprawiały wrażenia użytkownika, zwracały uwagę na ważne elementy i przekazywały informacje.

Twórcy stron internetowych mogą czerpać korzyści z połączenia mocy SVG i CSS w celu tworzenia animacji bez korzystania z zewnętrznych bibliotek. Ten samouczek dotyczący animacji SVG pokazuje, jak tworzyć niestandardowe animacje dla rzeczywistych projektów.

Animacja SVG przy użyciu CSS: podstawowe koncepcje

Przed animacją SVG za pomocą CSS programiści muszą zrozumieć, jak SVG działają wewnętrznie. Na szczęście jest podobny do HTML: definiujemy elementy SVG za pomocą składni XML i stylizujemy je za pomocą CSS, tak jak gdyby były HTML.

Elementy SVG są specjalnie zbudowane do rysowania grafiki. Możemy użyć <rect> do narysowania prostokąta, <circle> do narysowania okręgów itp. — SVG definiuje również <ellipse> , <line> , <polyline> , <polygon> i <path> .

Uwaga: pełna lista elementów SVG zawiera nawet element <animate> , który umożliwia tworzenie animacji przy użyciu zsynchronizowanego języka integracji multimediów (SMIL). Jednak jego przyszłość jest niepewna, a zespół Chromium zaleca stosowanie podejścia opartego na CSS lub JavaScript do animowania SVG, gdy tylko jest to możliwe.

Dostępne atrybuty zależą od elementu, więc podczas gdy <rect> ma atrybuty width i height , element <circle> ma atrybut r , który definiuje jego promień.

Trzy podstawowe elementy SVG (prostokąt, okrąg i linia) oraz ich dostępne atrybuty. Prostokąt jest zdefiniowany przez współrzędne x i y jego lewego górnego rogu, a także jego szerokość i wysokość, np. <rect x="25" y="25" width="150" height="100 "/>. Okrąg jest określony przez współrzędne x i y jego środka (cx, cy) oraz jego promień (r), np. <circle cx="75" cy="75" r="50"/>. Linia jest definiowana za pomocą współrzędnych początkowych (x1 i y1) i końcowych (x2 i y2), np. <line x1="40" y1="140 x2="140" y2="40"/>.
Wybierz podstawowe elementy SVG; współrzędne odnoszą się do początku (lewy górny róg okienka ekranu SVG).

Podczas gdy większość elementów HTML może mieć dzieci, większość elementów SVG nie. Jedynym wyjątkiem jest element grupy <g> , którego możemy użyć w celu zastosowania stylów CSS i przekształceń do wielu elementów jednocześnie.

Element <svg> i jego atrybuty

Inną ważną różnicą między HTML i SVG jest sposób, w jaki pozycjonujemy elementy, w szczególności za pomocą atrybutu viewBox danego zewnętrznego elementu <svg> . Jego wartość składa się z czterech liczb oddzielonych spacją lub przecinkiem: min-x , min-y , width i height . Razem określają one, jak dużą część naszego rysunku SVG ma renderować przeglądarka. Obszar ten zostanie przeskalowany, aby dopasować się do granic rzutni , zgodnie z definicją atrybutów width i height elementu <svg> .

Jeśli chodzi o letterboxy, stosunek atrybutów width i height widoku może rzeczywiście różnić się od stosunku części width i height atrybutu viewBox .

Domyślnie proporcje płótna SVG zostaną zachowane kosztem większego niż określono viewBox , powodując w ten sposób mniejsze renderowanie w formacie letterbox w widoku. Ale możesz określić inne zachowanie za pomocą atrybutu preserveAspectRatio .

Dzięki temu możemy rysować obrazy w izolacji i mieć pewność, że wszystkie elementy zostaną prawidłowo ustawione bez względu na kontekst lub rozmiar renderowania.

Obraz przedstawiający sposób renderowania viewBox w rzutni o innym współczynniku proporcji przy jednoczesnym zachowaniu proporcji zawartości. Po lewej stronie prostokątny viewBox ma wyśrodkowany sześcian izometryczny. Po prawej, większy kwadratowy rzutnia ma ten sam izometryczny sześcian, wyśrodkowany i powiększony, przy zachowaniu proporcji sześcianu.
Utrzymanie proporcji obrazu poprzez letterboxing.

Chociaż możesz ręcznie kodować obrazy SVG, bardziej złożone obrazy mogą wymagać programu do grafiki wektorowej (nasz samouczek dotyczący animacji SVG pokazuje obie techniki). Mój wybrany edytor to Affinity Designer, ale każdy edytor powinien zapewniać wystarczającą funkcjonalność dla prostych operacji omówionych tutaj.

Przejścia i animacje CSS

Przejścia CSS pozwalają nam określić szybkość i czas trwania zmian właściwości. Zamiast przeskakiwać natychmiast od wartości początkowej do wartości końcowej, wartości przechodzą płynnie, jak w tym przykładzie, w którym kolor koła SVG zmienia się po najechaniu na niego myszą:

Zobacz przykład Pen Transition autorstwa Filipa Defara (@dabrorius) na CodePen.

Możemy zdefiniować przejścia za pomocą właściwości transition , która przyjmuje nazwę właściwości, do której chcemy przejść, czas trwania przejścia, funkcję czasu przejścia (znaną również jako funkcja wygładzania) oraz długość opóźnienia przed rozpoczęciem efektu :

 /* property name | duration | easing function | delay */ transition: margin-right 4s ease-in-out 1s;

Możemy zdefiniować przejścia dla wielu właściwości CSS, z których każda może mieć osobne wartości przejść. Istnieją jednak dwa oczywiste ograniczenia tego podejścia.

Pierwszym ograniczeniem jest to, że przejścia są wyzwalane automatycznie, gdy zmienia się wartość właściwości. W niektórych przypadkach jest to niewygodne. Na przykład nie możemy mieć animacji, która zapętla się w nieskończoność.

Drugim ograniczeniem jest to, że przejścia zawsze mają dwa etapy: stan początkowy i stan końcowy. Możemy wydłużyć czas trwania animacji, ale nie możemy dodawać różnych klatek kluczowych.

Dlatego istnieje potężniejsza koncepcja: animacje CSS. Dzięki animacjom CSS możemy mieć wiele klatek kluczowych i nieskończoną pętlę:

Zobacz przykład Pen Animation autorstwa Filipa Defara (@dabrorius) na CodePen.

Aby animować właściwości CSS w wielu klatkach kluczowych, najpierw musimy zdefiniować klatki kluczowe za pomocą @keyframes . Czas trwania klatek kluczowych jest określony w jednostkach względnych (procentach), ponieważ w tym momencie nie zdefiniowaliśmy jeszcze czasu trwania animacji. Każda klatka kluczowa opisuje wartości co najmniej jednej właściwości CSS w danym momencie. Animacje CSS zapewnią płynne przejścia między klatkami kluczowymi.

Aplikujemy animację z opisanymi klatkami kluczowymi do żądanego elementu za pomocą właściwości animation . Podobnie do właściwości transition , akceptuje czas trwania, funkcję wygładzania i opóźnienie.

Jedyna różnica polega na tym, że pierwszym parametrem jest nasza nazwa @keyframes zamiast nazwy właściwości:

 /* @keyframes name | duration | easing-function | delay */ animation: my-sliding-animation 3s linear 1s;

Animowanie przełącznika menu hamburgera

Teraz, gdy mamy już podstawową wiedzę o tym, jak działa animacja SVG, możemy zacząć tworzyć klasyczną animację — przełącznik menu, który płynnie przechodzi między ikoną „hamburgera” a przyciskiem zamykania („X”):

Zobacz Pen Hamburger Filipa Defara (@dabrorius) na CodePen.

To subtelna, ale cenna animacja. Przyciąga uwagę użytkownika, informując, że ikona może posłużyć do zamknięcia menu.

Naszą demonstrację rozpoczynamy od utworzenia elementu SVG z trzema liniami:

 <svg class="hamburger"> <line x1="0" y1="50%" x2="100%" y2="50%" class="hamburger__bar hamburger__bar--top" /> <line x1="0" y1="50%" x2="100%" y2="50%" class="hamburger__bar hamburger__bar--mid" /> <line x1="0" y1="50%" x2="100%" y2="50%" class="hamburger__bar hamburger__bar--bot" /> </svg>

Każda linia ma dwa zestawy atrybutów. x1 i y1 reprezentują współrzędne początku linii, podczas gdy x2 i y2 reprezentują współrzędne końca linii. Do ustalenia pozycji użyliśmy jednostek względnych. Jest to prosty sposób na zapewnienie, że rozmiar zawartości obrazu zostanie zmieniony tak, aby pasował do zawierającego element SVG. Chociaż to podejście działa w tym przypadku, ma jedną dużą wadę: nie możemy zachować proporcji elementów ustawionych w ten sposób. W tym celu musielibyśmy użyć atrybutu viewBox elementu <svg> .

Zauważ, że zastosowaliśmy klasy CSS do elementów SVG. Istnieje wiele właściwości, które można zmienić za pomocą CSS, więc zastosujmy podstawową stylizację do naszych elementów SVG.

Ustawimy rozmiar elementu <svg> , a także zmienimy typ kursora, aby wskazać, że można go kliknąć. Ale aby ustawić kolor i grubość linii, użyjemy właściwości stroke i stroke-width . Mogłeś oczekiwać użycia color lub border , ale w przeciwieństwie do samego <svg> , podelementy SVG nie są elementami HTML, więc często mają różne nazwy właściwości:

 .hamburger { width: 62px; height: 62px; cursor: pointer; } .hamburger__bar { stroke: white; stroke-width: 10%; }

Jeśli wyrenderujemy w tym momencie, zobaczymy, że wszystkie trzy linie mają ten sam rozmiar i położenie, całkowicie zachodząc na siebie. Niestety nie możemy zmienić pozycji początkowej i końcowej niezależnie za pomocą CSS, ale możemy przenosić całe elementy. Przesuńmy górny i dolny pasek za pomocą właściwości transform CSS:

 .hamburger__bar--top { transform: translateY(-40%); } .hamburger__bar--bot { transform: translateY(40%); }

Przesuwając paski na osi Y otrzymujemy przyzwoicie wyglądający hamburger.

Teraz nadszedł czas na zakodowanie naszego drugiego stanu: przycisku zamykania. Polegamy na klasie CSS .is-opened zastosowanej do elementu SVG, aby przełączać się między tymi dwoma stanami. Aby wynik był bardziej dostępny, zapakujmy nasz SVG w element <button> i obsługujmy kliknięcia na tym poziomie.

Proces dodawania i usuwania klasy będzie obsługiwany przez prosty fragment kodu JavaScript:

 const hamburger = document.querySelector("button"); hamburger.addEventListener("click", () => { hamburger.classList.toggle("is-opened"); });

Aby stworzyć nasz X, możemy zastosować inną właściwość transform do naszych batoników hamburgerowych. Ponieważ nowa właściwość transform zastąpi starą, naszym punktem wyjścia będzie pierwotna, wspólna pozycja trzech słupków.

Stamtąd możemy obrócić górny pasek o 45 stopni zgodnie z ruchem wskazówek zegara wokół jego środka, a dolny pasek o 45 stopni w lewo. Możemy zmniejszyć środkowy pasek w poziomie, aż będzie wystarczająco wąski, aby był schowany za środkiem X:

 .is-opened .hamburger__bar--top { transform: rotate(45deg); } .is-opened .hamburger__bar--mid { transform: scaleX(0.1); } .is-opened .hamburger__bar--bot { transform: rotate(-45deg); }

Domyślnie właściwość transform-origin dla elementów SVG to zwykle 0,0 . Oznacza to, że nasze paski będą obracane wokół lewego górnego rogu rzutni, ale chcemy, aby obracały się wokół środka. Aby to naprawić, ustawmy właściwość transform-origin na center dla klasy .hamburger__bar .

Animowanie właściwości CSS z transition

Właściwość transition CSS informuje przeglądarkę o płynnym przechodzeniu między dwoma różnymi stanami właściwości CSS. Tutaj chcemy animować nasze zmiany we właściwości transform , która dyktuje pozycje, orientację i skalę słupków.

Możemy również kontrolować czas trwania przejścia za pomocą właściwości transition-duration . Aby animacja wyglądała zgryźliwie, ustawimy krótki czas trwania 0,3 sekundy:

 .hamburger__bar { transition-property: transform; transition-duration: 0.3s; ... }

Jedyny fragment kodu JavaScript, którego potrzebujemy, to ten bit, który umożliwia przełączanie stanu ikony:

 const hamburger = document.querySelector("button"); hamburger.addEventListener("click", () => { hamburger.classList.toggle("is-opened"); });

Tutaj wybieramy zewnętrzny element SVG przez jego klasę .mute za pomocą querySelector() . Następnie dodajemy detektor zdarzeń kliknięcia. Kiedy zdarzenie kliknięcia jest wyzwalane, przełączamy klasę .is-active tylko na samym <svg> — nie głębiej w hierarchii. Ponieważ animacja CSS ma zastosowanie tylko do elementów z klasą .is-active , przełączenie tej klasy będzie aktywować i dezaktywować animację.

Na koniec skonwertujemy treść HTML do elastycznego kontenera, co pomoże nam wyśrodkować ikonę w poziomie iw pionie. Zaktualizujemy również kolor tła na bardzo ciemnoszary, a kolor ikony na biały, aby uzyskać elegancki wygląd i styl w „trybie ciemnym”:

 body { display: flex; justify-content: center; align-items: center; background-color: #222; height: 100vh; }

Dzięki temu zbudowaliśmy w pełni funkcjonalny animowany przycisk, korzystając z podstawowego kodu CSS i krótkiego fragmentu kodu JavaScript. Łatwo jest zmienić transformacje, które zastosowaliśmy, aby stworzyć różnorodne animacje. Czytelnicy mogą po prostu rozwidlić CodePen — który zawiera trochę dodatkowego CSS do polerowania — i wykazać się kreatywnością.

Praca z danymi SVG z zewnętrznych edytorów

Nasze menu hamburgerowe jest niezwykle proste. A jeśli chcemy zrobić coś bardziej złożonego? W tym miejscu ręczne kodowanie SVG staje się trudne, a oprogramowanie do edycji grafiki wektorowej może pomóc.

Nasza druga animacja SVG to przycisk wyciszania z ikoną słuchawek. Gdy muzyka jest aktywna, ikona będzie pulsować i tańczyć; gdy jest wyciszony, ikona będzie przekreślona:

Zobacz przycisk wyciszania pióra - 5 - czerwone przekreślenie autorstwa Filipa Defara (@dabrorius) na CodePen.

Ikony do rysowania byłyby poza zakresem tego samouczka (i prawdopodobnie opisu twojej pracy), więc zaczniemy od wstępnie narysowanej ikony SVG. Będziemy również chcieli mieć taką samą stylizację body , jak nasze przykładowe menu z hamburgerami.

Możesz chcieć wyczyścić kod SVG przed rozpoczęciem pracy z nim. Możesz to zrobić za pomocą svgo, narzędzia optymalizującego SVG o otwartym kodzie źródłowym, opartego na Node.js. Usunie to niepotrzebne elementy i ułatwi ręczną edycję kodu, co trzeba będzie zrobić, aby dodawać klasy i łączyć różne elementy.

Ikony SVG utworzone w oprogramowaniu do edycji obrazu prawdopodobnie nie będą używać jednostek względnych. Dodatkowo chcemy mieć pewność, że proporcje ikony są zachowane, niezależnie od proporcji elementu SVG, który ją zawiera. Aby ten poziom kontroli był możliwy, użyjemy atrybutu viewBox .

Dobrym pomysłem jest zmiana rozmiaru SVG, aby viewBox mógł być ustawiony na kilka łatwych w użyciu wartości. W tym przypadku przekonwertowałem go na viewBox o wymiarach 100 x 100 pikseli.

Upewnijmy się, że ikona jest wyśrodkowana i ma odpowiednią wielkość. Zastosujemy klasę mute do naszego podstawowego elementu SVG, a następnie dodamy następujące style CSS:

 .mute { fill: white; width: 80px; height: 70px; cursor: pointer; }

Tutaj width jest nieco większa niż height , aby uniknąć przycinania podczas rotacji naszej animacji.

Nasz punkt początkowy animacji SVG

Czysty plik SVG zawiera pojedynczy element <g> , który zawiera trzy elementy <path> .

Element ścieżki pozwala nam rysować linie, krzywe i łuki. Ścieżki opisane są serią poleceń, które opisują sposób rysowania kształtu. Ponieważ nasza ikona składa się z trzech niepowiązanych ze sobą kształtów, mamy trzy ścieżki do ich opisania.

Element g SVG to kontener używany do grupowania innych elementów SVG. Używamy go, aby zastosować przemiany pulsujące i taneczne na wszystkich trzech ścieżkach jednocześnie.

 <svg class="mute" viewBox="0 0 100 100"> <g> <path d="M92.6,50.075C92.213,26.775 73.25,7.938 50,7.938C26.75,7.938 7.775,26.775 7.388,50.075C3.112,51.363 -0.013,55.425 -0.013,60.25L-0.013,72.7C-0.013,78.55 4.575,83.3 10.238,83.3L18.363,83.3L18.363,51.6C18.4,51.338 18.438,51.075 18.438,50.813C18.438,33.275 32.6,19 50,19C67.4,19 81.563,33.275 81.563,50.813C81.563,51.088 81.6,51.338 81.638,51.6L81.638,83.313L89.763,83.313C95.413,83.313 100.013,78.563 100.013,72.713L100.013,60.263C100,55.438 96.875,51.362 92.6,50.075Z" /> <path d="M70.538,54.088L70.538,79.588C70.538,81.625 72.188,83.275 74.225,83.275L74.225,83.325L78.662,83.325L78.662,50.4L74.225,50.4C72.213,50.4 70.538,52.063 70.538,54.088Z" /> <path d="M25.75,50.4L21.313,50.4L21.313,83.325L25.75,83.325L25.75,83.275C27.788,83.275 29.438,81.625 29.438,79.588L29.438,54.088C29.45,52.063 27.775,50.4 25.75,50.4Z" /> </g> </svg>

Aby słuchawki pulsowały i tańczyły, transition nie wystarczy. To jest przykład, który jest na tyle złożony, że wymaga klatek kluczowych.

W tym przypadku nasza początkowa i końcowa klatka kluczowa (odpowiednio w 0% i 100% animacji) używają lekko skurczonej ikony słuchawek. Przez pierwsze 40% animacji nieznacznie powiększamy obraz i przechylamy go o 5 stopni. Następnie przez kolejne 40% animacji przeskalujemy ją z powrotem do 0,9x i obrócimy o 5 stopni w drugą stronę. Wreszcie przez ostatnie 20% animacji transformacja ikony powraca do tych samych parametrów początkowych, aby płynnie zapętlać.

 @keyframes pulse { 0% { transform: scale(0.9); } 40% { transform: scale(1) rotate(5deg); } 80% { transform: scale(1) rotate(-5deg); } 100% { transform: scale(0.9) rotate(0); } }

Optymalizacje animacji CSS

Aby pokazać, jak działają klatki kluczowe, pozostawiliśmy nasz kod CSS klatek kluczowych bardziej gadatliwym, niż jest to konieczne. Można go skrócić na trzy sposoby.

Ponieważ nasza klatka kluczowa 100% ustawia całą listę transform , gdybyśmy mieli całkowicie pominąć rotate() , jej wartość byłaby domyślnie równa 0:

 100% { transform: scale(0.9); }

Po drugie, wiemy, że chcemy, aby nasze klatki kluczowe 0% i 100% pasowały do ​​siebie, ponieważ zapętlamy animację. Definiując je tą samą regułą CSS, nie będziemy musieli pamiętać o modyfikacji obu, jeśli chcemy zmienić ten wspólny punkt w pętli animacji:

 0%, 100% { transform: scale(0.9); }

Na koniec wkrótce zastosujemy transform: scale(0.9); do klasy mute__headphones , a kiedy to zrobimy, nie będziemy musieli w ogóle definiować początkowej i końcowej klatki kluczowej! Przyjmą one domyślnie styl statyczny używany przez mute__headphones .

Teraz, gdy zdefiniowaliśmy klatki kluczowe animacji, możemy zastosować animację. Dodajemy klasę .mute__headphones do elementu <g> , aby wpływała na wszystkie trzy części ikony słuchawek. Najpierw ponownie ustawiamy transform-origin na center , ponieważ chcemy, aby ikona obracała się wokół jej środka. Skalujemy go również tak, aby jego rozmiar odpowiadał początkowej klatce kluczowej animacji. Bez tego kroku przejście ze statycznej ikony „wyciszonej” na animowaną zawsze spowoduje nagły skok rozmiaru. (Tak czy inaczej, przełączenie z powrotem na wyciszenie spowoduje skok w skali — i prawdopodobnie także obrót — jeśli użytkownik kliknie, gdy skala jest większa niż 0,9x. Nie możemy wiele zdziałać z tym efektem z samym CSS.)

Animację stosujemy za pomocą właściwości CSS animation , ale tylko wtedy, gdy obecna jest klasa nadrzędna .is-active , podobnie jak animowaliśmy nasze menu hamburgerów.

 .mute__headphones { transform-origin: center; transform: scale(0.9); } .is-active .mute__headphones { animation: pulse 2s infinite; }

JavaScript, którego potrzebujemy, abyśmy mogli przełączać się między stanami, działa według tego samego wzorca, co menu hamburgerów:

 const muteButton = document.querySelector(".mute"); muteButton.addEventListener("click", () => { muteButton.classList.toggle("is-active"); });

Następnym elementem, który dodamy, jest przekreślona linia, która pojawia się, gdy ikona jest nieaktywna. Ponieważ jest to prosty element projektu, możemy go zakodować ręcznie. W tym miejscu przydatne jest posiadanie prostych i rozsądnych wartości viewBox . Wiemy, że krawędzie płótna są na 0 i 100, więc łatwo jest obliczyć pozycję początkową i końcową dla naszej linii:

 <line x1="12" y1="12" x2="88" y2="88" class="mute__strikethrough" />

Zmiana rozmiaru a używanie jednostek względnych

Można przedstawić przypadek użycia jednostek względnych zamiast zmiany rozmiaru obrazu. Dotyczy to naszego przykładu, ponieważ dodajemy tylko prostą linię SVG nad naszą ikoną.

W rzeczywistych scenariuszach możesz chcieć połączyć bardziej złożoną zawartość SVG z kilku różnych źródeł. W tym miejscu przydatne jest nadanie im jednolitego rozmiaru, ponieważ nie możemy ręcznie na stałe zakodować względnych wartości, jak to zrobiliśmy w naszym przykładzie.

Ponieważ zastosowaliśmy klasę bezpośrednio do naszego przekreślonego elementu <line> , możemy nadać jej styl za pomocą CSS. Musimy tylko upewnić się, że linia nie jest widoczna, gdy ikona jest aktywna:

 .mute__strikethrough { stroke: red; opacity: 0.8; stroke-width: 12px; } .is-active .mute__strikethrough { opacity: 0; }

Opcjonalnie możemy dodać klasę .is-active bezpośrednio do SVG. Spowoduje to, że animacja rozpocznie się natychmiast po załadowaniu strony, więc skutecznie zmienimy początkowy stan ikony z nieanimowanej (wyciszonej) na animowaną (nie wyciszone).

Animacja SVG oparta na CSS jest tutaj, aby pozostać

Tylko zarysowaliśmy powierzchnię technik animacji CSS i sposobu działania okienek ekranu. Warto wiedzieć, jak pisać ręcznie kod SVG, aby proste animacje były proste, ale ważne jest również, aby wiedzieć, jak i kiedy korzystać z grafiki stworzonej za pomocą zewnętrznych edytorów. Podczas gdy nowoczesne przeglądarki umożliwiają nam tworzenie imponujących animacji przy użyciu tylko wbudowanych funkcji, w (bardzo) złożonych przypadkach programiści mogą chcieć zapoznać się z bibliotekami animacji, takimi jak GSAP lub anime.js.

Animacje nie muszą być zarezerwowane dla ekstrawaganckich projektów. Nowoczesne techniki animacji CSS pozwalają nam tworzyć szeroką gamę angażujących i dopracowanych animacji w prosty, kompatybilny z różnymi przeglądarkami sposób.


Specjalne podziękowania dla Mike'a Zeballosa za recenzję techniczną tego artykułu!