Transition CSS3 i slider

Bardzo często proste sztuczki mogą nam mocno poprawić komfort pracy. Dzisiaj przyjrzymy się jednej z takich prostych, aczkolwiek magicznych rzeczy. Mowa o CSS3 transition, dzięki której jesteśmy w stanie animować nie tylko proste przejścia linków, ale także i całe skomplikowane layouty.

Reguła transition określa przejście elementu z jednego stanu do innego. Przykładowo:

a {
     color:blue;
     -moz-transition:0.5s;
     -webkit-transition:0.5s;
     transition:0.5s;
}
a:hover {
     color:red;
}

spowoduje, że link po najechaniu płynnie przejdzie z koloru niebieskiego na czerwony. Opisywać tej reguły po raz kolejny nie ma sensu. W necie znajdziesz na ten temat bardzo dużo artykułów, ot choćby pierwszy z brzegu.

Tak naprawdę regułę tę można wykorzystać nie tylko do prostych animacji linków po najechaniu, ale także do ciekawszych sposobów. Wystarczy zamiast :hover wykorzystać :focus, lub po prostu nadać elementowi dodatkową klasę:

.box {
    height:0;
    opacity:1;

    -moz-transition:1s;
    -webkit-transition:1s;
    transition:1s;
}
.box.active {
    height:auto;
    opacity:1;
}

Nadanie nowej klasy (u nas .active) powyższemu elementowi spowoduje ustawienie nowych wartości dla height i opacity, a dzięki temu, że element ma ustawione transtion, zmiana ta będzie płynnie animowana. Ot – prosta sztuczka, ale mająca bardzo duże możliwości.

W powyższym przypadku w javascript interesuje nas tylko włączenie/wyłączenie boxowi dodatkowej klasy:

$('.box').toggleClass('active'); //włączenie/wyłączenie klasy

np:

$(function() {
     $('.jakis-link-na-super-stronie').click(function(e) {
          e.preventDefault();
          $('.box').toggleClass('active'); //włączenie/wyłączenie klasy
     });
});

To wszystko.

Powyższy sposób nie tylko ułatwia nam kodowanie w js, ale ma także dodatkowe zalety.

  1. Oddziela nam skrypty od płaszczyzny wizualnej strony (w końcu w js przełączamy tylko klasę, a cała animacja zamiast w skryptach jest opisana w css).
  2. Animacja CSS jest o wiele płynniejsza i mniej wymagająca dla procesora niż taka sama animacja wykonana za pomocą JS. Bardzo fajnie opisał to Paul Irish w swoim artykule (http://paulirish.com/2012/why-moving-elements-with-translate-is-better-than-posabs-topleft/). Polecam także zerknięcie na ten artykuł: http://css3.bradshawenterprises.com/blog/jquery-vs-css3-transitions/.
    W skrócie wygląda to tak: gdy zmieniamy jakąś właściwość elementu (dla przykładu „left”), wtedy następuje „przerysowanie” layoutu strony. Jeżeli teraz wykonujemy w js animację elementu, musimy wykonywać takich przerysowań w krótkim czasie całkiem sporo, co bardzo spowalnia działanie strony. Dzięki temu, że do animacji zamiast skryptów wykorzystujemy CSS3 pozbywamy się tego problemu, gdyż animacje CSS są zarządzane przez przeglądarkę a nie wolne JS.

I właściwie tyle teorii. Jak widzisz – ogólnie zasada jest bardzo prosta. Całą animację wrzucamy do CSS ustawiając w jednej klasie stan początkowy (normalny), a w drugiej dodatkowej stan końcowy. Za pomocą JS tylko włączamy tą dodatkową klasę.

Za chwilę sprawdzimy to w nieco cięższym przypadku.

Slider w JS/CSS3

Powyższa metoda pozwala w naprawdę prosty sposób stworzyć efektowny slider, który dzięki animacjom CSS będzie bardziej wydajny niż niejedna kobyła popularna w internecie.

Na początku stwórzmy prosty kod slidera:

<section class="slider" id="slider">
     <h2 class="visualhidden">Slider</h2>

     <article class="slide active">
          <h2 class="title">Slide 1</h2>
          <div class="text">
               Lorem ipsum
          </div>
          <a href="" class="read-more">Czytaj więcej</a>
          <img src="images/slide-1.jpg" class="image" />
     </article><!-- e:slide -->

     <article class="slide">
          <h2 class="title">Slide 2</h2>
          <div class="text">
               Lorem ipsum
          </div>
          <a href="" class="read-more">Czytaj więcej</a>
          <img src="images/slide-1.jpg" class="image" />
     </article><!-- e:slide -->

     <ul class="pagination">
          <li><a href="">1</a></li>
          <li><a href="">2</a></li>
     </ul>

</section><!-- e:slider -->

Chyba nic nie trzeba wyjaśniać? Jest to prosty slider z dwoma slajdami i paginacją. Sam HTML może wyglądać bardziej tradycyjnie i składać się z divów, listy UL slajdów itp. Co kto woli.

Po kliknięciu na element paginacji odpowiedniemu slajdowi ustawimy klasę .active, a cały wygląd i animację wykonamy za pomocą CSS.

Podstawowe (początkowe) stylowanie dla tego slidera ma postać:

.visualhidden {
    visibility: hidden;
}

.slider {
     overflow: hidden;
     height:550px;
     width:100%;
     width:700px;
     position:relative;                       
     border:3px solid #333;
}
/* slajdy */
.slider .slide {
     position: absolute;
     top:0;
     left:0;
     height:555px;
     width:100%;
     opacity:0;
     z-index:1; /* wszystkie slajdy są ustawione "nisko" */
}

.slider .slide .image {
     position: absolute;
     top:0;
     right:-300px;
     opacity:0;  
     z-index:1; /* obrazki sa nizej niz teksty */

     -moz-transition:     1s;      -moz-transition-delay: 1.4s;
     -webkit-transition:  1s;   -webkit-transition-delay: 1.4s;
     transition:          1s;           transition-delay: 1.4s;
}

.slider .slide .title { 
     font:300 44px/1em 'Open Sans',sans-serif;
     width:500px;
     text-transform: uppercase;
     color:#333;
     margin:40px 0 30px 30px;
     opacity:0; 
     position: relative;
     left:-500px; /* wysuniete poza kadr */
     z-index:2;

     -moz-transition:    0.5s;      -moz-transition-delay: 0.5s;
     -webkit-transition: 0.5s;   -webkit-transition-delay: 0.5s;
     transition:         0.5s;           transition-delay: 0.5s;

     -moz-transform:scale(0.2);
     -webkit-transform:scale(0.2);     
     transform:scale(0.2);
}

.slider .slide .text {
     font:300 15px 'Open Sans', sans-serif;
     color:#555;
     position: relative;
     left:-500px; /* wysuniete poza kadr */
     width:500px;
     opacity:0;
     margin:0 0 30px 30px;
     z-index: 2;

     -moz-transition:    0.5s;      -moz-transition-delay: 0.7s;
     -webkit-transition: 0.5s;   -webkit-transition-delay: 0.7s;
     transition:         0.5s;           transition-delay: 0.7s;

     -moz-transform:scale(0.2);
     -webkit-transform:scale(0.2);
     transform:scale(0.2);
}

.slider .slide .read-more {
     font:600 15px/40px 'Open Sans', sans-serif;
     background:#799BCE;
     border-radius:5px;
     color:#fff;
     height:40px;
     text-decoration: none;
     display: inline-block;
     padding:0 30px;
     margin:0 0 0 30px;
     position: relative;
     left:-500px; /* wysuniete poza kadr */
     opacity:0; 
     z-index: 2;    

     -moz-transition:    0.5s;      -moz-transition-delay: 1s;
     -webkit-transition: 0.5s;   -webkit-transition-delay: 1s;
     transition:         0.5s;           transition-delay: 1s;

     -moz-transform:scale(0.2);
     -webkit-transform:scale(0.2);     
     transform:scale(0.2);
}

/* paginacja */
.slider .pagination {
    position: absolute;
    right:30px;
    bottom:30px;
    text-align: right;
    z-index: 30;
}

.slider .pagination li {
    width:26px;
    height:26px;
    margin-top:2px;
    background: #799BCE;
    display: inline-block;
    border-radius:50%;
    margin:0 3px;
}

.slider .pagination li.active {
    background: #B2CEFA;
    border:2px solid #B2CEFA;
}

.slider .pagination li a {
    text-align: left;
    display: block;
    text-indent:-999px;
    overflow:hidden;
    width:100%;
    height:100%;
}

Wszystkie slajdy są pozycjonowane absolutnie względem .slidera. Elementy każdego slajda: tytuł h2 .title, .text, .read-more oraz .image są wysunięte poza kadr (dzięki ustawieniu ujemnych właściwości left, right).

Po zaaplikowaniu danemu slajdowi klasy .active zmienimy ich właściwości tak by znalazły się w kadrze aktualnego slajda, dzięki czemu uzyskamy animację wjeżdżania tych elementów na slajd:

/* slajd aktywny */
.slider .slide.active {
     opacity:1;  /* pokazujemy slajd */
     z-index:10; /* aktualny slajd nad resztą slajdów */
}

.slider .slide.active .image {
     opacity:1; /* wjazd na kadr */
     right:0;
}

.slider .slide.active .title {
     left:0;  /* wjazd na kadr */
     opacity:1;

     -webkit-transform:scale(1);
     -moz-transform:scale(1);
     transform:scale(1);
}

.slider .slide.active .text {
     opacity:1; /* wjazd na kadr */
     left:0;

     -moz-transform:scale(1);
     -webkit-transform:scale(1);
     transform:scale(1);
}

.slider .slide.active .read-more {
     opacity:1; /* wjazd na kadr */
     left:0;

     -webkit-transform:scale(1);
     -moz-transform:scale(1);
     transform:scale(1);
}

To wszystko!

Od tego momentu każdy aktualny slajd (.active) będzie płynnie pojawiał się w kadrze (opacity:1), a jego elementy równie ładnie wjadą na kadr.

Pozostaje nam tylko podpięcie przełączania slajdów pod paginację i jesteśmy prawie w domu. Możemy wykorzystać do tego któryś z trików z :checkboxem (z których zresztą już korzystaliśmy w przypadku tworzenia responsywnego menu), albo napisać prosty skrypt.
Ja osobiście wybieram drugą metodę, dzięki temu nie zaśmiecam kodu html jakimiś elementami formularza (ble!), a i całość wydaje mi się bardziej logiczna:

$(function() {

     $('#slider .pagination li').click(function (e) {
          e.preventDefault();

          var $this = $(this);
          var $li = $this.parent().find('li');
          var index = $li.index($this);

          $('#main .slide').removeClass('active').eq(index).addClass('active');

          $li.removeClass('active');
          $this.addClass('active');
     });

});

Tak naprawdę w powyższym kodzie przełączyliśmy tylko klasę .active na slajd o wybranym (klikniętym) indeksie.

Automatyczne przełączanie

Ostatnią rzeczą jaką powinniśmy/moglibyśmy dodać do naszego slidera, to automatyczne przełączanie slajdów. I to zadanie nie jest ciężkie:

$(function() {

     var pauseTime = 3000;

     $('#slider .pagination li').click(function (e) {
          e.preventDefault();
          //po kliknięciu czyścimy timer
          clearTimeout(timer);

          var $this = $(this);
          var $li = $this.parent().find('li');
          var index = $li.index($this);

          $('#slider .slide').removeClass('active').eq(index).addClass('active');

          $li.removeClass('active');
          $this.addClass('active');

          //włączamy timer do kolejnego kliknięcia
          timer = setTimeout(function() {
               var index = $li.index($li.parent().find('.active'));
               var clickLi = (index>=$li.length-1)? 0 : ++index;

               $('#slider .pagination li').eq(clickLi).click();
          }, pauseTime);
     });

     //po wejściu na stronę odpalamy pierwszy timer, który kliknie na drugi element paginacji
     var timer = setTimeout(function() {
          $('#main-slider .pagination li').eq(1).click();
     }, pauseTime);

});

Za pomocą setTimeout odpalamy co jakiś czas kliknięcie odpowiedniego LI paginacji. Po każdym kliknięciu czyścimy zegar odmierzający czas do kliknięcia, przełączamy slajd, po czym znowu odmierzamy czas do kolejnego kliknięcia. Jest to typowe działanie większości sliderów. Zresztą już to kiedyś przerabialiśmy :)

Pokaż demo

A co z IE?

Ah. Nasza kochana przeglądarka, która w wersji <=8 nie obsługuje transition. W powyższym sliderze przełączanie slajdów będzie działać jak najbardziej, ale użytkownicy starych IE nie zobaczą żadnej animacji. Jeżeli chcemy ją wymuszać, wtedy za pomocą modernizera musimy wykryć czy dana przeglądarka obsługuje transition, i jeżeli wynik jest negatywny „musimy” animację wykonać za pomocą skryptów.
Specjalnie użyłem cudzysłowów, bo tak naprawdę czy będziemy wymuszali animację dla starszych przeglądarek jest tylko naszą decyzją. Coraz więcej świata zaczyna wychodzić z założenia, że wspieranie starszych IE jest błędem. Przykładem może być Google, czy kochana przez nas biblioteka jQuery, która od wersji 2 przestanie wspierać stare przeglądarki.

 

No dobrze. My tu gadu, gadu, ale powiedzmy, że mimo wszystko jednak chcemy animować. Wchodzimy więc na modernizer.com, budujemy naszą bibliotekę wybierając interesujące nas rzeczy (dla tego przykładu napewno będzie to CSS Transitions z pierwszej kolumny), a następnie po podpięciu jej do naszej strony piszemy skrypt dla IE:

if(!Modernizr.csstransitions) {
     ....
}

Cały skrypt może mieć postać:

<script src="modernizr.custom.81340.js"></script>
<script>
$(function() {

    var pauseTime = 6000;

        $('#slider .pagination li').click(function (e) {
            e.preventDefault();
            //po kliknięciu czyścimy timer
            clearTimeout(timer);

            var $this = $(this);
            var $li = $this.parent().find('li');
            var index = $li.index($this);
            var $currentSlide = $('#slider .slide:eq('+index+')');

            if(Modernizr.csstransitions) { //jeżeli jest transition

                $('#slider .slide').removeClass('active').eq(index).addClass('active');

            } else { //jeżeli nie ma działamy ręcznie

                //czyścimy do stanu początkowego przed dodaniem klasy
                $('#slider .slide').removeClass('active');
                $('#slider .slide').css({opacity:0});
                $('#slider .slide').find('.title, .text, .read-more').css({left: -500, opacity:0});                
                $('#slider .slide').find('.image').css({right: -300, opacity:0});
                $currentSlide.addClass('active');                

                //zaczynamy animację
                $currentSlide.animate({opacity:1}, function() {
                    var $slide = $(this);                    
                    $slide.find('.title').delay(50).animate({left : 0, opacity: 1}, 500);
                    $slide.find('.text').delay(70).animate({left : 0, opacity: 1}, 500);
                    $slide.find('.read-more').delay(100).animate({left : 0, opacity: 1}, 500);
                    $slide.find('.image').delay(140).animate({right : 0, opacity: 1}, 1000);                                                   
                });

            }

            //włączamy timer do kolejnego kliknięcia
            timer = setTimeout(function() {
                var index = $li.index($li.parent().find('.active'));
                var clickLi = (index>=$li.length-1)? 0 : ++index;

                $('#slider .pagination li').eq(clickLi).click();
            }, pauseTime);

            $li.removeClass('active');
            $this.addClass('active');

        });

        //po wejściu na stronę odpalamy pierwszy timer, który kliknie na drugi element paginacji
        var timer = setTimeout(function() {
             $('#main-slider .pagination li').eq(1).click();
        }, pauseTime);

});
</script>

Jak widzisz, „nieco” się to rozrosło. Głównie dlatego, że musimy resetować stan slajda, potem go animować itp. Nie jest to optymalna metoda, a tylko pokazująca, że animacja za pomocą jquery wcale taka przyjemna nie jest, a zastąpienie jej animacją CSS jest o wiele wygodniejsze.

Pokaż demo

Podsumowanie

Dzisiaj znajoma zapytała mnie jak jest tworzony efekt na stronie: http://www.templatemonster.com/demo/41359.html. Gdy jej powiedziałem, że wystarczy do tego 4-5 liniowy skrypt, nie chciała uwierzyć. Jeżeli jednak wykorzystamy do tego celu powyższą metodę, wtedy kilka linijek JS załatwi sprawę. Wystarczy tylko obsłużyć click, wykonać jquerowe load, po którym dodać klasę w odpowiednie miejsce.

Tak naprawdę celem powyższego tekstu nie było pokazanie ci jak zrobić konkretny slider. Chodzi o idee wykorzystania transition (lub animacji CSS3) wraz z pojedynczą dodatkową klasą-przełącznikiem. Ja już od jakiegoś czasu przestałem męczyć się pisaniem animacji w JS i polegam na CSS3. Czas i na ciebie :)

Edit: Ciekawą prezentację o powyższej technice możecie znaleźć tutaj: http://www.impressivewebs.com/jquery-toronto-slides-video/

 

Komentarze

  • mercs600

    Jestem tutaj pierwszy raz, i żałuję – dobra wiedza w języku polskim , będę tutaj podbijał częściej :)

  • Pingback: Stylowanie formularzy - doman.art.pl()

  • Stlife

    Genialnia strona, az mi sie morda zaczela cieszyc jak zobaczylem takie rozwiazania w css3 :D

  • Paweł

    U mnie automatyczne przełączanie startuje dopiero po kliknięciu na paginację, tak jakby przeglądarka nie potrafiłą wystartować ze sliderem.

    • Shopen

      //po wejściu na stronę odpalamy pierwszy timer, który kliknie na drugi element paginacji
      var timer = setTimeout(function() {
      $(‚#main-slider .pagination li’).eq(1).click();
      }, pauseTime);

      wystraczy ‚#main-slider’ zamienić na ‚#slider’

  • Pingback: Memory w javascript - domanart.pl()

    • kartofelek007

      Ja do tej pory tylko liznąłem flexboxa. W krótkich testach bardzo mi się podobał, chociaż zdaje mi się, że trzeba będzie znowu przerobić swoje myślenie tak jak w przypadku table->div, tylko desktopy->urządzenia różne, cukierki czekoladowe->marchewka itp :)
      Z tym font-size:0 gdzieś mi się obiło, że na androidzie czy gdzieś tam też są z tym problemy.

  • Kordian Bober

    Jak przerobić ten slider żeby uruchamiał się od razu, bez klikania?

  • Kordian Bober

    Wiecie może jak wprowadzić opcję „następny slajd” na przycisk (nie że wybierasz kropkę z konkretnym slajdem, po prostu następny)

    • Dodajesz 2 przyciski .prev i .next i do nich dodajesz kod js:


      $('.next').on('click', function(e) {
      e.preventDefault();
      var $li = $('#slider .pagination li');
      var $slides = $('#slider .slide');
      var index = $slides.index($slides.filter('.active'));
      if (index =$li.length-1)? 0 : ++index;

      $('#slider .pagination li').eq(clickLi).click();
      }, pauseTime);
      })

      $('.prev').on('click', function(e) {
      e.preventDefault();
      var $li = $('#slider .pagination li');
      var $slides = $('#slider .slide');
      var index = $slides.index($slides.filter('.active'));
      if (index > 0) {
      index -= 1;
      } else {
      index = $slides.length - 1;
      }
      $slides.removeClass('active');
      $slides.eq(index).addClass('active');

      $li.removeClass('active');
      $li.eq(index).addClass('active');

      clearTimeout(time);
      timer = setTimeout(function() {
      var index = $li.index($li.parent().find('.active'));
      var clickLi = (index>=$li.length-1)? 0 : ++index;

      $('#slider .pagination li').eq(clickLi).click();
      }, pauseTime);
      })

      Na szybko pisane i sprawdzone, wiec trzeba by jeszcze się przypatrzec tej metodzie, ewentualnie ją zoptymalizować poprzez wyniesienie powtórek do funkcji