CSS3 - Animowana karuzela 3D

W tej części kursu CSS utworzymy kolejny układ elementów HTML w przestrzeni perspektywy.

Tym razem zajmiemy się karuzelą, która została zaprezentowana poniżej.

Zanim zaczniemy pisać odpowiedni kod, który pozwoli nam utworzyć nasz docelowy układ elementów HTML, spróbujemy sobie wyjaśnić pewne reguły, które rządzą otaczającym nas światem, a o których niekoniecznie musimy mieć pojęcie.

Załóżmy, że na naszym biurku leży dziewięć kart o tych samych rozmiarach szerokości i wysokości. Załóżmy, że wpadliśmy na genialny pomysł, aby poukładać nasze karty, na naszym biurku, w ten sposób, aby utworzyły zamknięty okrąg.

W jaki sposób to uczynimy?

Możemy położyć pudełko od naszych kart na środku naszego biurka, a następnie wokół wspomnianego pudełka, możemy spróbować utworzyć nasz docelowy układ kart, czyli zamknięty okrąg, który ma składać się z dziewięciu kart. Po kilku próbach udaje się nam, dzięki czemu na naszym biurku znajduje się następujący układ kart:

karuzela kart - przykład 1

Nasze karty, których jest dziewięć, utworzyły okrąg, który możemy zaznaczyć w następujący sposób:

karuzela kart - przykład 2

Każda karta styka się rogami z sąsiadującymi kartami. Każda karta ma ten sam rozmiar oraz została ona odsunięta od środka okręgu o ten sam odcinek.

Odcinek ten musimy w jakiś sposób odnaleźć, ponieważ sposób na ustalenie wartości wspomnianego odcinka przyda się nam, gdy zaczniemy budować karuzelę 3D, która będzie składała się z prostokątnych elementów HTML.

Szukany odcinek został zaznaczony strzałką koloru czarnego.

karuzela kart - przykład 3

Czas na trochę matematyki.

Wiemy, że okrąg ma 360 stopni. W naszym układzie znalazło się 9 kart. W tym momencie możemy odgadnąć wartość kąta o jaki została obrócona każda z kolejnych kart, ponieważ każda z nich ma ten sam rozmiar 120px × 60px i została odsunięta od środka okręgu o tą samą odległość.

Dzielimy 360 stopni przez ilość kart, czyli 9, co daje nam 40 stopni, dzięki czemu wiemy, że każda kolejna karta została obrócona o 40 stopni więcej od poprzedniej, czyli o 0 stopni, 40 stopni, 80 stopni, 120 stopni, 160 stopni itd. aż do dziewiątej wartości równej 320 stopni.

Przyglądając się temu, w jaki sposób każda kolejna karta jest odsunięta od środka okręgu, w układzie kart na naszym biurku, możemy wyznaczyć trójkąt równoramienny, którego kąt wierzchołka będzie wynosił 40 stopni, ponieważ każda kolejna karta w naszym układzie została obrócona właśnie o wielokrotność liczby 40, o czym była mowa w poprzednim akapicie. Natomiast wartość podstawy naszego trójkąta równoramiennego będzie równa wartości wysokości naszej karty, czyli 60px.

karuzela kart - przykład 4

Wartość wysokości naszego trójkąta równoramiennego będzie naszą szukaną wartością odległości karty od środka okręgu.

Wysokość trójkąta równoramiennego dzieli ten trójkąt na dwa trójkąty prostokątne. Wartość kąta wierzchołka trójkąta prostokątnego jest o połowę mniejsza od wartości kąta wierzchołka trójkąta równoramiennego, czyli w naszym przykładzie tą wartością jest wartość 20, ponieważ 40 / 2 = 20. Jak łatwo się domyśleć podstawa naszego trójkąta prostopadłego będzie wynosiła połowę wartości 60px, czyli 30px.

Dzięki w ten sposób odszukanym wartością możemy skorzystać z funkcji trygonometrycznych kąta ostrego w trójkącie prostokątnym, dzięki czemu odszukamy wartość odległości karty od środka okręgu.

karuzela kart - przykład 5

Po obliczeniu na kalkulatorze działania 30 / tan(20) wskaże on nam szukaną wartość, która wynosi w przybliżeniu 82, a więc każda z naszych kart została odsunięta od środka okręgu o wartość 82px.

Skoro wiemy już, jak poradzić sobie z odszukaniem wartości odległości kart od środka okręgu, możemy zacząć budowę karuzeli 3D, która będzie składać się z prostokątnych elementów HTML.

Zaczniemy od utworzenia znanego nam układu elementów HTML, z poprzedniej części tego działu, a więc umieszczamy element div w elemencie div, do którego dodajemy dziewięć elementów div, które utworzą naszą docelową karuzelę.

<body>

  <div id="perspektywa">
    <div id="rodzic">
      <div></div>
      <div></div>
      <div></div>
      <div></div>
      <div></div>
      <div></div>
      <div></div>
      <div></div>
      <div></div>
    </div>
  </div>

</body>

Do elementu #perspektywa dodajemy właściwość perspective:1200px; dzięki czemu w ten sposób sprawimy, że element #rodzic znajdzie się w przestrzeni perspektywy, która będzie wynosiła 1200px.

Aby we wspomnianej perspektywie znalazły się również dzieci elementu #rodzic należy dodać do niego właściwość transform-style:preserve-3d; o czym była mowa w poprzedniej części tego działu.

Dla elementu #perspektywa oraz wszystkich elementów div umieszczonych w elemencie #rodzic ustalamy jednakowe rozmiary oraz dodajemy inne znane nam właściwości CSS, które sprawią, że wszystkie elementy HTML znajdą się w tym samym miejscu (działanie właściwości position:absolute; powinno być już dla nas znane).

#perspektywa {
  width:120px;
  height:60px;
  margin:0 auto;
  perspective:1200px;
}

#rodzic {
  transform-style:preserve-3d;
}

#rodzic > div {
  position:absolute;
  width:118px;
  height:58px;
  border:1px solid #000;
  background-color:rgba(255,255,255,0.5);
  background-image:radial-gradient(ellipse at top left,transparent,#00F);
  box-shadow:0 0 4px #00F;
}

Rezultat:

Dzieci elementu #rodzic znalazły się jeden na drugim, dzięki właściwości position:absolute; znalazły się one również w przestrzeni perspektywy ich rodzica, czyli elementu #rodzic, ponieważ wspomniany element HTML posiada w sobie właściwość transform-style:preserve-3d;.

Czas ustalić kąt o jaki obrócimy, wokół osi Y, każdy kolejny element div, który znajduje się w elemencie #rodzic, za pomocą funkcji rotateY().

Skoro wszystkie interesujące nas elementy div mają ten sam rozmiar, a okrąg, po którym będzie poruszać się nasza docelowa karuzela, ma 360 stopni, to dzielimy tą wartość przez ilość elementów HTML, które będą poruszać się po okręgu, czyli przez liczbę 9. W ten sposób otrzymujemy wartość 40.

Każdy kolejny element div, który znajduje się w elemencie #rodzic, obracamy wokół osi Y, za pomocą funkcji rotateY(), o 40 stopni więcej od poprzedniego elementu w naszym układzie. Dodatkowo cały układ możemy nieco obrócić do dołu. Efekt ten uzyskamy za pomocą funkcji rotateX(), którą należy dodać do rodzica elementów HTML, które będą tworzyć karuzelę, czyli do elementu #rodzic.

#rodzic {
  transform:rotateX(-7deg);
  transform-style:preserve-3d;
}

#rodzic > div:nth-child(1) {
  transform:rotateY(0);
}

#rodzic > div:nth-child(2) {
  transform:rotateY(40deg);
}

#rodzic > div:nth-child(3) {
  transform:rotateY(80deg);
}

#rodzic > div:nth-child(4) {
  transform:rotateY(120deg);
}

#rodzic > div:nth-child(5) {
  transform:rotateY(160deg);
}

#rodzic > div:nth-child(6) {
  transform:rotateY(200deg);
}

#rodzic > div:nth-child(7) {
  transform:rotateY(240deg);
}

#rodzic > div:nth-child(8) {
  transform:rotateY(280deg);
}

#rodzic > div:nth-child(9) {
  transform:rotateY(320deg);
}

Rezultat:

Jedyne co nam pozostało to ustalić odległość o jaką ma zostać odsunięty każdy element div, który jest dzieckiem elementu #rodzic. Wspomniane odsunięcie wykonamy za pomocą funkcji translateZ(), dzięki której element HTML zostanie przesunięty w przestrzeni perspektywy wzdłuż własnej osi Z.

Wiemy, że musimy skorzystać z następującego wzoru: wartość / tan(kąt), gdzie wartość w tym wypadku będzie to połowa wartości szerokości danego elementu HTML, czyli 120px / 2 = 60px. Natomiast kąt w tym wypadku to połowa kąta, o który obracaliśmy każdy kolejny element HTML, czyli 40 / 2 = 20.

Na kalkulatorze obliczamy 60 / tan(20), co daje nam w przybliżeniu 164.

Dodajemy funkcję translateZ(164px) do każdego elementu div, który został umieszczony w elemencie #rodzic, Funkcję translateZ() należy podać po funkcji rotateY(), ponieważ w tym wypadku funkcja rotateY() wyznaczy kierunek, w który ma zostać przesunięty element HTML w przestrzeni perspektywy, za pomocą funkcji translateZ() i właśnie o taki efekt nam chodzi.

#rodzic > div:nth-child(1) {
  transform:rotateY(0) translateZ(164px);
}

#rodzic > div:nth-child(2) {
  transform:rotateY(40deg) translateZ(164px);
}

#rodzic > div:nth-child(3) {
  transform:rotateY(80deg) translateZ(164px);
}

#rodzic > div:nth-child(4) {
  transform:rotateY(120deg) translateZ(164px);
}

#rodzic > div:nth-child(5) {
  transform:rotateY(160deg) translateZ(164px);
}

#rodzic > div:nth-child(6) {
  transform:rotateY(200deg) translateZ(164px);
}

#rodzic > div:nth-child(7) {
  transform:rotateY(240deg) translateZ(164px);
}

#rodzic > div:nth-child(8) {
  transform:rotateY(280deg) translateZ(164px);
}

#rodzic > div:nth-child(9) {
  transform:rotateY(320deg) translateZ(164px);
}

Rezultat:

Naszą karuzelę wprawimy w ruch za pomocą następującej animacji:

@keyframes obrot
{
  0% { transform:rotateX(-7deg) rotateY(0); }
  100% { transform:rotateX(-7deg) rotateY(360deg); }
}

Animacja obrot sprawi, że element będzie cały czas obrócony względem własnej osi X o 7 stopni oraz będzie obracał się wokół własnej osi Y o 360 stopni. Wspomnianą animację należy dodać do elementu #rodzic, dzięki czemu on oraz jego cała zawartość, którą są elementy div budujące naszą karuzelę, zacznie się obracać.

#rodzic {
  transform:rotateX(-7deg);
  transform-style:preserve-3d;
  animation:obrot 8s linear infinite;
}

Finalny rezultat:

Kod dokumentu HTML został zaprezentowany poniżej:

<!DOCTYPE html>
<html>
  <head>
    <meta charset="utf-8">
    <title>Darmowy Kurs CSS</title>

    <style>
      #perspektywa {
        width:120px;
        height:60px;
        margin:0 auto;
        perspective:1200px;
        -webkit-perspective:1200px;
      }

      #rodzic {
        transform:rotateX(-7deg);
        -webkit-transform:rotateX(-7deg);
        transform-style:preserve-3d;
        -webkit-transform-style:preserve-3d;
        animation:obrot 8s linear infinite;
        -webkit-animation:obrot 8s linear infinite;
      }

      #rodzic > div {
        position:absolute;
        width:118px;
        height:58px;
        border:1px solid #000;
        background-color:rgba(255,255,255,0.5);
        background-image:radial-gradient(ellipse at top left,transparent,#00F);
        box-shadow:0 0 4px #00F;
      }


      #rodzic > div:nth-child(1) {
        transform:rotateY(0) translateZ(164px);
        -webkit-transform:rotateY(0) translateZ(164px);
      }

      #rodzic > div:nth-child(2) {
        transform:rotateY(40deg) translateZ(164px);
        -webkit-transform:rotateY(40deg) translateZ(164px);
      }

      #rodzic > div:nth-child(3) {
        transform:rotateY(80deg) translateZ(164px);
        -webkit-transform:rotateY(80deg) translateZ(164px);
      }

      #rodzic > div:nth-child(4) {
        transform:rotateY(120deg) translateZ(164px);
        -webkit-transform:rotateY(120deg) translateZ(164px);
      }

      #rodzic > div:nth-child(5) {
        transform:rotateY(160deg) translateZ(164px);
        -webkit-transform:rotateY(160deg) translateZ(164px);
      }

      #rodzic > div:nth-child(6) {
        transform:rotateY(200deg) translateZ(164px);
        -webkit-transform:rotateY(200deg) translateZ(164px);
      }

      #rodzic > div:nth-child(7) {
        transform:rotateY(240deg) translateZ(164px);
        -webkit-transform:rotateY(240deg) translateZ(164px);
      }

      #rodzic > div:nth-child(8) {
        transform:rotateY(280deg) translateZ(164px);
        -webkit-transform:rotateY(280deg) translateZ(164px);
      }

      #rodzic > div:nth-child(9) {
        transform:rotateY(320deg) translateZ(164px);
        -webkit-transform:rotateY(320deg) translateZ(164px);
      }

      @keyframes obrot
      {
        0% { transform:rotateX(-7deg) rotateY(0); }
        100% { transform:rotateX(-7deg) rotateY(360deg); }
      }

      @-webkit-keyframes obrot
      {
        0% { -webkit-transform:rotateX(-7deg) rotateY(0); }
        100% { -webkit-transform:rotateX(-7deg) rotateY(360deg); }
      }
    </style>
  </head>

  <body>
  
    <div id="perspektywa">
      <div id="rodzic">
        <div></div>
        <div></div>
        <div></div>
        <div></div>
        <div></div>
        <div></div>
        <div></div>
        <div></div>
        <div></div>
      </div>
    </div>

  </body>
</html>

...::| C D N |::...