Webdesign & UX

Prosta animacja z wykorzystaniem SVG i JavaScript

Wykorzystując grafikę wektorową możemy osiągać bardzo ciekawe efekty małym nakładem pracy, na pewno jest to wygodniejsza forma niż dziesiątki kontenerów i setki linii kodu CSS. Przygotowałem efekt obracających się trybików. Animacja jest płynna przez co przyjemna dla oka i nie obciąża zanadto procesora. Nie jest to oczywiście pokaz wszystkich możliwości duetu SVG i JavaScript, ale może zainspiruje Cię do stworzenia prawdziwego działa sztuki.

SVG

Tym razem nie zaczniemy od kodu HTML, a od stworzenia trybiku. W tym celu użyjemy programu Inkscape. Interfejs jest intuicyjny, a obsługa samego programu dziecinnie prosta. Trybik stworzymy z koła i kształtu podobnego do prostokąta z zaokrąglonymi rogami jako ikona 64×64.

Gdy już stworzyliśmy kształt pozostało tylko skopiować do schowka jego ścieżkę (nie chodzi o ścieżkę do pliku, a ścieżkę kształtu). Edytor XML znajduje się w menu Edycja programu Inkscape.

HTML

Skoro mamy już ścieżkę możemy teraz utworzyć plik kod pliku SVG. Nie pójdziemy jednak na łatwiznę i nie skopiujemy dwukrotnie pliku SVG, gdyż to nie w naszym stylu. Stworzymy najpierw „szablon”, z którego będziemy korzystać tworząc trybiki.

<svg width="0" height="0" style="position:absolute;margin:-100%;">
  <path id="cog-icon" d="M 31.84375 2 C 30.444245 2.028643 29.053781 2.0360801 28.125 3 C 26.717268 4.4609873 26.873078 8.4713374 27.0625 10.6875 C 25.273051 11.092878 23.500812 11.743426 21.84375 12.59375 C 20.577229 10.769876 18.124455 7.640283 16.125 7.4375 C 14.720156 7.2950221 13.587191 8.2606095 12.46875 9.25 C 11.414861 10.182286 10.349218 11.094945 10.25 12.4375 C 10.100081 14.466134 12.726485 17.421584 14.28125 19 C 13.129576 20.522353 12.213366 22.18139 11.5 23.875 C 9.3676995 23.301928 5.5311391 22.451487 3.875 23.59375 C 2.708326 24.398422 2.4470743 25.920814 2.21875 27.40625 C 2.0036034 28.805951 1.754477 30.188255 2.53125 31.28125 C 3.6923356 32.915007 7.5122442 33.483609 9.71875 33.6875 C 9.80532 35.567508 10.141644 37.442892 10.6875 39.25 C 8.6804174 40.208077 5.2872163 42.100172 4.75 44.03125 C 4.3673957 45.406557 5.1063713 46.68232 5.875 47.96875 C 6.5992652 49.180931 7.3044473 50.386739 8.59375 50.71875 C 10.516538 51.21389 13.802504 49.18597 15.625 47.90625 C 16.253211 48.591841 16.930076 49.289629 17.65625 49.90625 C 18.381663 50.522224 19.130827 51.055903 19.90625 51.5625 C 18.977176 53.605849 17.562605 57.208517 18.375 59.03125 C 18.955491 60.33367 20.375318 60.889511 21.78125 61.375 C 23.106034 61.832468 24.395196 62.303074 25.59375 61.71875 C 27.381878 60.846991 28.635779 57.134195 29.21875 54.96875 C 31.067048 55.19714 32.910561 55.208092 34.75 54.96875 C 35.332971 57.134195 36.586872 60.846991 38.375 61.71875 C 39.646966 62.338866 41.052115 61.823864 42.4375 61.28125 C 43.742923 60.76995 45.046762 60.258499 45.59375 59.03125 C 46.411167 57.197248 44.960032 53.567233 44.03125 51.53125 C 45.563772 50.523336 46.993643 49.293907 48.28125 47.875 C 50.091223 49.15151 53.432605 51.218938 55.375 50.71875 C 56.743275 50.366405 57.502149 49.066828 58.21875 47.75 C 58.893989 46.509176 59.579271 45.327179 59.21875 44.03125 C 58.670224 42.059517 55.143347 40.125714 53.15625 39.1875 C 53.696809 37.388727 54.040398 35.541105 54.125 33.6875 C 56.308275 33.494061 60.254745 32.945497 61.4375 31.28125 C 62.261853 30.121309 62.006238 28.63113 61.71875 27.15625 C 61.447853 25.766498 61.193085 24.351979 60.09375 23.59375 C 58.41517 22.436008 54.487667 23.30101 52.375 23.875 C 51.669132 22.189606 50.746518 20.557385 49.625 19.0625 C 51.170583 17.501563 53.870628 14.492652 53.71875 12.4375 C 53.613457 11.012709 52.46956 10.005318 51.3125 9.0625 C 50.222222 8.1740976 49.167511 7.3032447 47.84375 7.4375 C 45.824164 7.6423245 43.34856 10.84149 42.09375 12.65625 C 40.436746 11.793681 38.677421 11.129859 36.90625 10.71875 C 37.097122 8.5093377 37.257959 4.4677093 35.84375 3 C 34.858078 1.9770373 33.328979 1.9696017 31.84375 2 z M 32 22 C 37.522847 22 42 26.477153 42 32 C 42 37.522847 37.522847 42 32 42 C 26.477153 42 22 37.522847 22 32 C 22 26.477153 26.477153 22 32 22 z "/>                   
</svg>

Kolejnym krokiem będzie napisanie kodu właściwego dla każdego trybiku.

<div id="cogs">
  <svg id="cog1" class="cog" viewBox="0 0 64 64">
    <use xlink:href="#cog-icon"></use>
  </svg>
  <svg id="cog2" class="cog" viewBox="0 0 64 64">
    <use xlink:href="#cog-icon"></use>
  </svg>
</div>

CSS

Jedyna sprawa, którą warto poruszyć przy tym banalnym kawałku kodu jest właściwość fill. Jeżeli nie używałeś wcześniej SVG to raczej się z nią nie spotkałeś. Odpowiada ona za wypełnienie kształtu.

body {
  background: #0D6068;
}
#cogs {
  position: absolute;
  top: 30px;
  left: 30px;
}
.cog{
  width: 128px;
  height: 128px;
  fill: #55D0DB;
  position: absolute;
}
#cog1 {
  top: 0;
  left: 0;
}
#cog2 {
  top: 55px;
  left: 95px;
}

JavaScript

Kod JavaScript też jest nieskomplikowany. Co 10ms obracamy trybiki o 1 stopień.

var rotate_right = $('#cog1 use');
var rotate_left = $('#cog2 use');            
setInterval(function(){
    if(deg >= 360) deg = 0;
    deg += 1;
    rotate_right.attr('transform', 'rotate('+deg+',32,32)');
    rotate_left.attr('transform', 'rotate('+-deg+',32,32)');
}, 10);

Jeżeli nie ograniczyłeś się tylko do kopiowania kodu to na pewno zauważyłeś, że w CSS nadaliśmy ikonom wymiary 128×128 pikseli, a mimo to działamy na nich tak jak by były czterokrotnie mniejsze. Dlaczego? Pamiętaj, że ikona została stworzona jako obraz o rozmiarach 64×64 piksele i to ten wymiar decyduje o tym jak przeglądarka potraktuje obraz.

Przydatne linki

komentarzy 5

  • Awatar
    Comandeer

    7 grudnia 2013 21:01

    http://css-tricks.com/svg-tabs-using-svg-shape-template/ ;) ale sam pomysł jest genialny.

    ciekawi mnie dlaczego format grafiki od wieków miał wbudowany w sobie system szablonów a HTML dochrapał się go dopiero ostatnio, pod postacią tagu template i link[rel=import]. inna rzecz, że SVG nigdy nie zdobył wcześniej takiej popularności (ciekawe czemu, prawda Microsoft?)

    wartałoby także zaznaczyć, że domyślnie SVG pracować powinien tylko w trybie XML a jego inlinowanie w HTML-u dostępne jest teoretycznie dopiero w HTML 5. no i przy większych zabawach z SVG możemy dojść do problemów z wydajnością (bo to DOM w końcu)

    co do samego JS – AFAIR minimalnym czasem odstępu dla timerów jest 20ms i żaden browser poniżej tego pułapu nie schodzi (stąd też takie usilne parcie na to, żeby Chrome i lisek w końcu wprowadziły wsparcie dla setImmediate, zwłaszcza, że polyfille dla nich działają szybciej od natywnej implementacji w IE…). zresztą dla animacji i tak istnieje requestAnimationFrame i to właśnie jego powinno się używać. w połączeniu z techniką delta timing pozwala tworzyć płynniejsze animacje (bo tutaj będziemy mieli przestój i już płynność wiadomo co trafi). no i zaciągnięcie jQuery dla ustawienia wartości CSS dwóch elementów mnie drażni. bardzo…

    Odpowiedz
    • Awatar
      Michał Załęcki

      8 grudnia 2013 00:23

      Niezastąpiony Comandeer :)

      Zmieniłem kod o tyle, by nie szukał za każdym razem trybika tylko zapisałem je w zmiennych. Patrząc prawdzie w oczy to przy większych zabawach z czymkolwiek dochodzimy do problemów z wydajnością (np. checkboxy wyświetlające piłkę, wrzucałem to na webroadowego facebooka kilka dni temu). Ja nie mam problemów z zejściem poniżej 10ms, ani na lisku, ani na IE11 na desktopie, ani na IE10 na telefonie. Korzystając z requestAnimationFrame, o ile się nie mylę, żegnamy się z IE9 więc sobie to odpuściłem na wstępie. Ja przy jQuery będę się upierał. Na tym prostym przykładzie z dwoma trybikami może faktycznie nie widać wyższości jQuery nad rozwiązaniem bez niej, ale na swojej stronie mam kilka takich trybików i mogę bez problemu przeprowadzić niejawną iterację. W „czystym” JS będę już musiał wprowadzić pętlę i tracę tym samym na prostocie kodu. Myślę, że do poradnika takie rozwiązanie bardziej przemawia do ludzi.

      Odpowiedz
      • Awatar
        Comandeer

        8 grudnia 2013 00:28

        wiem, że był problem z tym zejściem. no a requestFrameAnimation ma tą przewagę nad timerami, że korzysta z pętli rysującej (tej samej co CSS), więc AFAIR ma wspomaganie przez GPU. no i nie ma takiej rygorystycznej natury, co one co pomaga przy animowaniu.
        a co do IE 9 – polyfill jest banalny ;) window.requestAnimationFrame=function(fn){setTimeout(fn,1000/60);}; tak najprościej mówiąc
        a w jQuery pętli nie ma? ;) w JS przecież też można ładnie
        [].forEach.call(document.querySelectorAll(‚.nasze#trybiki’),function(t){t.setAttribute(‚transform’,’blabla’);});
        może nie jest aż tak pięknie jak w jQuery, ale wciąż czytelnie

        Odpowiedz
      • Awatar
        Comandeer

        23 grudnia 2013 19:17

        Jest problem z zejściem ;) Ale nie jest to próg 10ms, jak wcześniej sądziłem. Otóż minimalny czas oczekiwania w przypadku timerów jest określony w specyfikacji HTML5 i wynosi 4ms. https://developer.mozilla.org/en-US/docs/Web/API/window.setTimeout#Minimum.2F_maximum_delay_and_timeout_nesting

        Odpowiedz
  • Awatar
    piotrabc

    23 lutego 2014 23:48

    Hej! Jest szybszy sposób na trybik w Inkscape: Efekty -> Renderowanie -> Koło Zębate.
    Wtedy cała metoda staje się jeszcze szybsza.
    Przydatny art. Pozdrawiam

    Odpowiedz

Zostaw odpowiedź