Animacje w jQuery

W bardzo wielu przypadkach będziemy chcieli dane elementy strony animować. Możemy to zrobić za pomocą metody animate(properties, time*, ease*, fn*). Najbardziej interesującymi nas parametrami są properties, do których przekazujemy obiekt składający się z właściwości i ich nowych wielkości:

Zarówno w JS, jQuery czy CSS możemy dodawać animacje na divy, spany i podobne elementy. Pamiętaj jednak, że większość takich elementów nie jest dostępna dla użytkowników poruszających się po stronie za pomocą klawiatury. Interaktywne elementy powinny być tworzone za pomocą odpowiednich znaczników takich jak a, button, input itp.

<button type="button" class="button" id="testAnim">Animuj</button>
<div id="testAnimBlock" class="test-block">DIV</div>

$("#testAnim1").on('click', function(){
    $(this).animate({
        width: "500px",
        opacity: 0.4,
        fontSize: "3em",
        borderWidth: "10px"
    }, 1500);
});
Kliknij mnie

Parametr ease określa przebieg animacji.
jQuery posiada wbudowane dwa rodzaje przebiegu animacji: linear i swing. Aby skorzystać z innych rodzajów, powinniśmy skorzystać z dodatkowych pluginów do jQuery, np. tego: http://gsgd.co.uk/sandbox/jquery/easing/.


$("#testAnim2").on('click', function(){
    $(this).animate({
            height:200,
            width:400,
            opacity: 0.5
        },
        {
            duration: 1000, //czas animacji
            ease: 'linear', //typ animacji
            complete: function() { //funkcja zwrotna
                alert("koniec animacji");
            }
        }
    );
});
Kliknij tutaj

Po dodaniu powyższej biblioteki z serwera CDN (np stąd) możemy użyć o wiele więcej typów animacji:

Kliknij tutaj

$("#testAnim2").on('click', function(){
    $(this).animate({
            height:200,
            width:400,
            opacity: 0.5
        },
        {
            duration: 1000, //czas animacji
            easing: 'easeOutBounce', //typ animacji
            complete: function() { //funkcja zwrotna
                alert("koniec animacji");
            }
        }
    );
});

Uproszczony zapis

Powyższy zapis definicji animacji mimo, że czytelny, jest nieco długi. W wielu sytuacjach będziesz chciał zastosować uproszczony zapis:


$('.module1').animate({width: 500});

$('.module2').animate({width: 500}, 1000);

$('.module3').animate({width: 500}, 1000, function() {
    console.log('koniec animacji');
});

Poza właściwościami wszystkie atrybuty są opcjonalne, więc możemy ich nie podawać. Jak widzisz, nie musimy nawet przekazywać obiektu z ustawieniami, a tylko podać je w odpowiedniej kolejności.

Inne opcje dla wartości

W powyższych przykładach animowane właściwości ustawiłem na sztywno.
W jQuery można także zastosować wartości relatywne za pomocą zapisów -= lub +=, które odejmą lub dodadzą odpowiednią wielkość od aktualnej:


$("#testAnim4").on('click', function(){
    $(this).animate({
        width: "+=" + 50,
        height: "+=" + 10,
        opacity: "-=" + 0.1,
        duration : 2000 //inny sposób deklaracji czasu trwania animacji
    });
});
Poklikaj tutaj szybko kilka razy

Poprawa animacji

Przyjrzyjmy się teraz pewnej sytuacji.
Mamy blok, po najechaniu na który chcemy płynnie zmienić jego rozmiary w zależności od tego czy kursor na nim jest czy nie. Po zjechaniu kursorem animacja powinna wrócić do początkowego stanu:


$('#testAnim5').on({
    'mouseover' : function() {
        $(this).animate({
            width: 300
        }, 800);
    },
    'mouseout' : function() {
        $(this).animate({
            width: 200
        }, 800);
    }
});
Pomęcz mnie

Problem pojawi się wtedy, gdy użytkownik zacznie się pastwić nad naszym elementem szybko najeżdżając i opuszczając go kursorem. Kilka takich ruchów i animacja się zapętla, mimo, że już dawno jesteśmy kursorem w innym miejscu. Spowodowane jest to tym, że nasza animacja nie zdąży się do końca wykonać, a my chcemy odpalić ją ponownie.

Aby temu zapobiec musimy posiłkować się jedną z dwóch technik.

Pierwsza z nich polega na wykorzystaniu metody stop(), która zatrzyma odpaloną animację:


$("#testAnim6").on({
    'mouseover' : function() {
        $(this).stop().animate({width:300}, 500);
    },
    'mouseout' : function() {
        $(this).stop().animate({width:200}, 500);
    }
});
Pomęcz mnie

Druga metoda polega na sprawdzaniu czy dany element nie zakończył swojej animacji:


$("#testAnim7").on('click', function(){
    if (!$(this).is(':animated')) {
        $(this).animate({
            width: "+=" + 50,
            height: "+=" + 10,
            opacity: "-=" + 0.1,
            duration : 3000 //inny sposób deklaracji czasu trwania animacji
        });
    }
});
Poklikaj tutaj szybko kilka razy

Zauważ, że w przeciwieństwie do jednego z poprzednich przykładów teraz nawet jak będziesz szybko klikał, to po przerwaniu tego szaleństwa (...) div nie będzie kontynuował swojego rozrostu.

Powyższe dwa sposoby sprawdzają się w innych zastosowaniach. Metoda stop() lepiej sprawdza się w zdarzeniach mouseover i mouseout, natomiast sprawdzanie czy obiekt jest animowany lepiej stosować przy zdarzeniach click

Delay

Metoda delay(time) służy do opóźniania kolejnych czynności w składni łańcuchowej (gdzie kolejne polecenia są łączone kropką):


$('.element')
    .hide()
    .delay(2000) //czekam 2 sekundy
    .slideDown();

Metoda ta ma szczególne zastosowanie gdy chcemy dla przykładu rozwinąć kolejne elementy dodawane do dynamicznej listy.

Wyobraź sobie, że masz listę użytkowników:


const users = [
    { name : "Marcin", surname : "Nowak" },
    { name : "Piotr", surname : "Kowalski" },
    { name : "Grzegorz", surname : "Piotrowski" },
    { name : "Karol", surname : "Karolak" },
    { name : "Michał", surname : "Nowacki" }
]

Chcielibyśmy zrobić pętlę po takiej tablicy, dynamicznie wygenerować dla każdego użytkownika kontener i wrzucić go do listy:


$(".add-button").on('click', function() {
    const $userList = $('.user-list');

    users.forEach(function(user) {
        const html = `<div class="user-cnt">
            <strong class="user-name-label">Nazwa użytkownika:</strong>
            <span class="user-name">${user.name} ${user.surname}</span>
        </div>`;

        const $cnt = $(html);
        $userList.append($cnt)
    });
});
Nazwa użytkownika: Marcin Domański

Jak widzisz dodawanie działa, ale nie jest wizualnie dobre dla użytkownika. O wiele lepszym rozwiązaniem będzie rozwijać dodawane elementy:


const $userList = $('.user-list');

$(".add-button").on('click', function() {
    users.forEach(function(user) {
        const html = `<div class="user-cnt">
            <strong class="user-name-label">Nazwa użytkownika:</strong>
            <span class="user-name">${user.name} ${user.surname}</span>
        </div>`;

        const $cnt = $(html).hide();
        $userList.append($cnt);
        $cnt.slideDown();
    });
});
Nazwa użytkownika: Marcin Domański

Problem z tym podejściem jest taki, że wszystkie elementy są rozwijane w tym samym momencie. I tutaj właśnie idealnym rozwiązaniem będzie użycie delay(), tak by elementy rozwijały się jeden po drugim:


const $userList = $('.user-list');

$(".add-button").on('click', function() {
    users.forEach(function(user, i) {
        const html = `<div class="user-cnt">
            <strong class="user-name-label">Nazwa użytkownika:</strong>
            <span class="user-name">${user.name} ${user.surname}</span>
        </div>`;

        const $cnt = $(html).hide();
        $userList.append($cnt);
        $cnt.delay(300*i).slideDown();
    });
});
Nazwa użytkownika: Marcin Domański