Animacje w SVG
Do tej pory zajmowaliśmy się wyłącznie statyczną grafiką. Czas wprowadzić trochę ruchu za pomocą specjalnych narzędzi animacji w SVG oraz skryptów.
<?xml version="1.0"?> <!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"> <svg version="1.1" xmlns="http://www.w3.org/2000/svg" width="250" height="100"> <title>Prosta Animacja</title> <rect x="10" y="10" width="200" height="20" style="stroke: blue; fill: orange;"> <animate attributeName="width" attributeType="XML" from="200" to="20" begin="0s" dur="5s" fill="freeze" /> </rect> </svg>Jak widać znacznika <animate> znajduje się wewnątrz prostokąta, który będziemy animować. Ustawienie attributeName="width" oznacza, że zmianie będzie podlegał atrybut width, który jest atrybutem XML (attributeType="XML"). Szerokość prostokąta będzie się zmieniać od 200 do 20, czyli będzie się zmniejszać. Wszystko będzie trwać 5 sekund (begin="0s" dur="5s"). Ponadto po zakończeniu animacji zostanie wyświetlona jej ostatnia klatka (dzięki atrybutowi freeze). Pierwszą i ostatnią klatkę animacji przedstawiają poniższe rysunki. Do jednego elementu możemy również zastosować wiele znaczników <animate>. Spójrzmy na poniższy przykład.
<?xml version="1.0"?> <!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"> <svg version="1.1" xmlns="http://www.w3.org/2000/svg"> <title>Animacja 2</title> <rect x="20" y="20" width="10" height="10" style="stroke: silver; fill: green; style: fill-opacity: 0.25;"> <animate attributeName="width" attributeType="XML" from="10" to="250" begin="0s" dur="8s" fill="freeze"/> <animate attributeName="height" attributeType="XML" from="10" to="200" begin="0s" dur="8s" fill="freeze"/> <animate attributeName="fill-opacity" attributeType="CSS" from="0.25" to="1" begin="0s" dur="3s" fill="freeze"/> <animate attributeName="fill-opacity" attributeType="CSS" from="1" to="0.25" begin="3s" dur="3s" fill="freeze"/> <animate attributeName="height" attributeType="XML" from="200" to="10" begin="8s" dur="14s" fill="freeze"/> <animate attributeName="width" attributeType="XML" from="250" to="10" begin="8s" dur="14s" fill="freeze"/> </rect> </svg>Omówię krótko jak będzie się animował powyższy prostokąt. Atrybut width (atrybut XML) zmienia się od 10 do 250 w czasie 8 sekund. Podobnie atrybut height zmienia się od 10 do 200 w czasie 8 sek. Kolejnym atrybutem podlegającym zmianom jest fill-opacity (jest to atrybut CSS) zmienia się od 0.25 do 1 czyli zwiększa się nieprzezroczystość. Zaczyna się to w zerowej sekundzie i trwa 3 sek. Następnie znowu zmieniamy atrybut fill-opacity, tym razem od 1 do 0.25, zaczynając w trzeciej sekundzie. Animacja ta trwa 3 sek (dur="3s"). Dwa ostatnie znaczniki <animate> odpowiadają za ponowne zmiany atrybutów width i height. Tym razem prostokąt zmniejsza swoje rozmiary, począwszy od 8 sekundy i trwa to 14 sek.
Możemy również animować wiele obiektów na raz. Ilustruje to poniższy przykład, w którym animujemy jednocześnie prostokąt i koło.
<?xml version="1.0"?> <!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"> <svg version="1.1" xmlns="http://www.w3.org/2000/svg"> <title>Animacja 2</title> <rect x="20" y="20" width="10" height="10" style="stroke: silver; fill: green; style: fill-opacity: 0.25;"> <animate attributeName="width" attributeType="XML" from="10" to="250" begin="0s" dur="8s" fill="freeze"/> <animate attributeName="height" attributeType="XML" from="10" to="200" begin="0s" dur="8s" fill="freeze"/> <animate attributeName="fill-opacity" attributeType="CSS" from="0.25" to="1" begin="0s" dur="3s" fill="freeze"/> <animate attributeName="fill-opacity" attributeType="CSS" from="1" to="0.25" begin="3s" dur="3s" fill="freeze"/> <animate attributeName="height" attributeType="XML" from="200" to="10" begin="8s" dur="14s" fill="freeze"/> <animate attributeName="width" attributeType="XML" from="250" to="10" begin="8s" dur="14s" fill="freeze"/> </rect> <circle cx="100" cy="140" r="10" style="stroke: black; fill: lightblue; style: fill-opacity: 0.25;"> <animate attributeName="r" attributeType="XML" from="10" to="100" begin="0s" dur="8s" fill="freeze"/> <animate attributeName="fill-opacity" attributeType="CSS" from="0.25" to="1" begin="0s" dur="3s" fill="freeze"/> <animate attributeName="fill-opacity" attributeType="CSS" from="1" to="0.25" begin="3s" dur="3s" fill="freeze"/> <animate attributeName="r" attributeType="XML" from="100" to="10" begin="8s" dur="14s" fill="freeze"/> </rect> </svg>W przypadku prostokąta animacja jest taka sama jak w poprzednim przykładzie. Dodajemy tutaj koło, dla którego zmienia się podczas animacji promień (atrybut r) oraz przezroczystość (fill-opacity). Czas animacji mierzony jest od momentu załadowania dokumentu SVG, aż do chwili kiedy użytkownik opuści stronę. My możemy określić początek i czas trwania poszczególnych części animacji w jeden z następujących sposobów:
- wartości w godzinach, minutach i sekundach (1:25:31),
- wartości minut i sekund (5:30),
- wartości czasowe w jednostkach czasu, gdzie h (godzina), min (minuta), s (sekunda), ms (milisekunda), np. dur="4.5s" lub begin="1min". Zapis liczby i jednostki nie może zawierać spacji.
<?xml version="1.0"?> <!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"> <svg version="1.1" xmlns="http://www.w3.org/2000/svg"> <title>Animacja 4</title> <circle cx="60" cy="60" r="30" style="fill: #f9f; stroke: gray;"> <animate id="c1" attributeName="r" attributeType="XML" begin="0s" dur="4s" from="30" to="10" fill="freeze"/> </circle> <circle cx="120" cy="60" r="10" style="fill: #9f9; stroke: gray;"> <animate attributeName="r" attributeType="XML" begin="c1.end" dur="4s" from="10" to="30" fill="freeze"/> </circle> </rect> </svg>Rysunek poniżej przedstawia kluczowe klatki tej animacji. Fragmenty kodu koloru granatowego odpowiadają za to, że animacja drugiego z kół rozpoczyna się wtedy, gdy pierwsze (o id="c1") przestaje się animować. Dokładniej odpowiada za to instrukcja begin="1.end".
Przykładowo aby dodać 2 sekundy opóźnienia dla animacji, która ma wystąpić po innej animacji należy wpisać: begin="inna_animacja.end+2s".
Można również synchronizować animacje przy pomocy atrybutu end, ustawiając czas końca animacji. Spójrzmy na poniższy kod. Poniższa animacja rozpoczyna się 10 sekund po załadowaniu strony i będzie trwała 15 sekund lub kiedy inna_animacja się skończy.
<animate attributeName="width" attributeType="XML" begin="10s" dur="15s" end="inna_animacja.end" from="10" to="100" fill="freeze"/>Oczywiście możemy jako wartość atrybutu end podac również liczbę (np. 10s). Do tej pory nasze animacje odtwarzane były tylko jednokrotnie. Dzięki ustaleniu wartości atrybutu fill na freeze otrzymaliśmy efekt końcowy animacji. Pozostawiając wartość domyślną remove, powrócilibyśmy do pierwszej klatki. Teraz poznamy dwa inne atrybuty, które pozwalają powtarzać animację. Pierwszym z nich jest repeatCount. Jego wartością może być liczba naturalna, określająca ile razy animacja ma być powtarzana. Jeżeli przypiszemy temu atrybutowi wartość indefinite animacja będzie odtwarzana w nieskończoność. Drugi atrybut to repeatDur, który określa jak długo powtórzenie ma trwać (również może przyjąć wartość indefinite). W przykładzie poniżej animacja koła jest powtarzana 3 razy.
<?xml version="1.0"?> <!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"> <svg version="1.1" xmlns="http://www.w3.org/2000/svg"> <title>Animacja 5</title> <circle cx="60" cy="60" r="10" style="fill: none; stroke: black; fill: green"> <animate attributeName="r" attributeType="XML" begin="0s" dur="5s" repeatCount="3" from="10" to="40" fill="freeze"/> </circle> </svg>Tutaj natomiast animacja powtarza się przez 7 sekund, co w rezultacie daje około 1,5 raza.
<?xml version="1.0"?> <!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"> <svg version="1.1" xmlns="http://www.w3.org/2000/svg"> <title>Animacja 5</title> <circle cx="100" cy="100" r="10" style="fill: #ccf; stroke: black;"> <animate attributeName="r" attributeType="XML" begin="0s" dur="5s" repeatDur="7s" from="10" to="45" fill="freeze"/> </circle> </svg>Możemy również łączyć synchronizację animacji z ich powtarzalnością. Załóżmy, że nadaliśmy pierwszej animacji atrybut id="anim1". Ustawimy teraz aby następna animacja rozpoczęła się wtedy gdy pierwsza powtórzy się 2 razy + 2.5 sek. Musimy po prostu dla drugiej animacji wstawić atrybut begin="circle-anim.repeat(2) + 2.5s". Wszystkie z powyższych animacji modyfikowaliśmy za pomocą liczbowych wartości określających czas. Czasami istnieje jednak potrzeba aby użyć innych wartości. Na przykład można sterować widzialnością danego obiektu. Służy do tego znacznik <set>. Potrzebuje on tylko atrybutu to (bez from) i cechy (np. visibility). Ilustruje to przykład. Animacja rozpoczyna się 0.5 sek po załadowaniu strony i trwa 1 sek, aż tekst się pojawi (to="visible").
<?xml version="1.0"?> <!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"> <svg version="1.1" xmlns="http://www.w3.org/2000/svg"> <title>Animacja 7</title> <text text-anchor="middle" x="80" y="60" style="visibility: hidden; font-size: 24px"> <set attributeName="visibility" attributeType="CSS" to="visible" begin="0.5s" dur="1s" fill="freeze"/> Visible text </text> </svg>Za pomocą zwykłego znacznika <animate> nie możemy manipulować kolorami. Z pomocą przychodzi nam <animateColor>. W wartościach atrybutów from i to tego tagu podajemy po prostu kolory. W poniższym przykładzie zmienia się zarówno kolor wypełnienia, jak i obrysu kwadratu.
<?xml version="1.0"?> <!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"> <svg version="1.1" xmlns="http://www.w3.org/2000/svg"> <title>Animacja 8</title> <rect x="70" y="60" width="60" height="60" style="fill: #ff9; stroke: gray; stroke-width: 15;"> <animateColor attributeName="fill" begin="1s" dur="4s" from="#ff9" to="magenta" fill="freeze"/> <animateColor attributeName="stroke" begin="1s" dur="4s" from="green" to="blue" fill="freeze"/> </rect> </svg>Kolejnym ciekawym znacznikiem jest <animateTransform>. Służy on do animacji wykorzystając atrybut translate. W poniższej animacji wykorzystujemy skalowanie (scale) jako typ transformacji.
<?xml version="1.0"?> <!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"> <svg version="1.1" xmlns="http://www.w3.org/2000/svg"> <title>Animacja 9</title> <circle transform="scale(1)" style="fill: green;" cx="150" cy="150" r="100"> <animateTransform attributeName="transform" attributeType="XML" type="scale" begin="1s" from="1" to="2" dur="10s" fill="freeze" /> </circle> </svg>Oczywiście możemy zastosować wiele elementów <animateTransform> do jednego obiektu. Teraz zajmiemy się animacjami po ścieżce. Do implementacji takiej animacji służy znacznik <animateMotion>. Poniżej przedstawiam przykład animacji koła po ścieżce. Jako atrybut w <animateMotion> podajemy ścieżkę (path), po której obiekt ma się poruszać.
<?xml version="1.0"?> <!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"> <svg version="1.1" xmlns="http://www.w3.org/2000/svg"> <title>Animacja 10</title> <path d="M50,125 C 100,25 150,225, 200, 125" style="fill: none; stroke: blue;"/> <circle r="10" style="fill: yellow; stroke: blue;"> <animateMotion path="M50,125 C 100,25 150,225, 200, 125" dur="6s" fill="freeze"/> </circle> </svg>