Zegary

Nadszedł czas. W tym artykule podejmiemy wyzwanie stworzenia i animacji zegara za pomocą prostych animacji CSS oraz języka JavaScript, aby je wywołać.

Oto zegar, który stworzymy za pomocą HTML, CSS, tła SVG i języka JavaScript. Użyjemy animacji CSS lub przejść dla każdego ruchu i oprzemy się na JavaScript, aby ustawić czas początkowy dodając podstawowe przejścia CSS.

HTML

Rozpoczniemy od fragmentu kodu HTML.

<article class="clock">
  <div class="hours-container">
    <div class="hours"></div>
  </div>
  <div class="minutes-container">
    <div class="minutes"></div>
  </div>
  <div class="seconds-container">
    <div class="seconds"></div>
  </div>
</article>

Moim pierwszym podejściem było użycie trzech elementów dla każdej wskazówki. Następnie wróciłem i owinąłem każdy w element kontenera. Chodź prostszy HTML działał w miarę jak podstawowe animacje CSS, będziemy potrzebować elementy zawierające, jeśli chcemy umieścić wskazówki i zaanimować je.

Tarcza zegara

Rozpoczniemy od podstawowego wyglądu zegara z okrągłą tarczą, prostymi wskazówkami godzin, minut i sekund.

.clock {
  border-radius: 50%;
  background: #fff url(/assets/images/posts/clocks/ios_clock.svg) no-repeat center;
  background-size: 88%;
  height: 20em;
  padding-bottom: 31%;
  position: relative;
  width: 20em;
}

.clock.simple:after {
  background: #000;
  border-radius: 50%;
  content: "";
  position: absolute;
  left: 50%;
  top: 50%;
  transform: translate(-50%, -50%);
  width: 5%;
  height: 5%;
  z-index: 10;
}

Możesz pobrać tło SVG tutaj. Dodałem również pseudo-element, aby dodać czarne koło na środek. Wskazówki zegara mogą być umieszczone pod tym pseudo-elementem w razie potrzeby.

Powinnyśmy teraz mieć coś takiego:

Przed dodaniem wskazówek, musimy umieścić kontenery.

.minutes-container, .hours-container, .seconds-container {
  position: absolute;
  top: 0;
  right: 0;
  bottom: 0;
  left: 0;
}

To układa każdy kontener na wierzchu zegara. Następnie tworzymy wskazówki.

Wskazówka godzin

Każda wskazówka otrzymała właściwość pozycji absolute i została umieszczona na godzinie dwunastej. Zaczniemy od wskazówki godzin.

.hours {
  background: #000;
  height: 20%;
  left: 48.75%;
  position: absolute;
  top: 30%;
  transform-origin: 50% 100%;
  width: 2.5%;
}

Używam wartości procentowych, aby skalowanie zegarów było łatwiejsze. To wymaga trochę więcej pracy, ale ułatwia dostosowanie wyglądu do urządzeń mobilnych. Ustawimy również właściwość transform-origin na dole wskazówki tak, aby można było ją później obracać.

Wskazówka minut

Wskazówka minut jest podobna, ale wyższa i cieńsza.

.minutes {
  background: #000;
  height: 40%;
  left: 49%;
  position: absolute;
  top: 10%;
  transform-origin: 50% 100%;
  width: 2%;
}

Wskazówka sekund

Wskazówka sekund jest jeszcze cieńsza, ale również wystaje poza środek. Aby to zrobić ustawiłem transform-origin na 80%. To sprawia, że 20% wskazówki wystaje poza środek.

.seconds { background: #000; height: 45%; left: 49.5%; position: absolute; top: 14%; transform-origin: 50% 80%; width: 1%; z-index: 8; }

Animacja

Stojący zegar będzie wskazywał prawidłową godzinę tylko dwa razy dziennie. Dodajmy animację, aby zegar zachowywał się bardziej prawidziwie.

Niektóre zegary skaczą co sekundę wytwarzając dźwięk tykania. Inne mruczą wraz z płynnym poruszaniem się wskazówek. Wypróbujemy obu sposobów. Najpierw sprawimy, że wskazówki będą poruszały się płynnie.

Możemy użyć keyframe, aby przekazać wskazówkom, aby obracały się o 360 stopni (zakładamy 0% jako pozycję startową).

@keyframes rotate { 100% { transform: rotateZ(360deg); } }

Ta klatka kluczowa mówi elementowi, aby obracał się o 360 stopni po zastosowaniu dla elementu właściwości animation. Wykorzystamy funkcję czasu linear, aby wskazówka poruszała się płynnie.

.hours-container { animation: rotate 43200s infinite linear; } .minutes-container { animation: rotate 3600s infinite linear; } .seconds-container { animation: rotate 60s infinite linear; }

Wskazówka hours jest ustawiona tak, aby wykonywać pełny obrót co 12 godzin (43,200 sekund). Wskazówka minut wykonuje pełny obrót co godzinę, a wskazówka sekund co minutę.

Składając to razem otrzymujemy ruch.

Jeśli masz bystre oko, możesz nawet zauważyć ruch wskazówki minut. Pełny obrót zajmie godzinę oraz dwanaście godzin w przypadku wskazówki godzin.

Wskazówka sekund wykonuje pełny obrót w 60 sekund, więc łatwiej ją zauważyć.

Dodawanie kroków

Możemy sprawić, że wskazówki będą zachowywać się jak w zwykłym zegarze tworząc 60 osobnych ruchów dla wskazówki sekund. Prostym sposobem na osiągnięcie tego jest użycie funkcji czasowej steps. Właściwość animation dla każdej wskazówki wygląda tak:

.minutes-container { animation: rotate 3600s infinite steps(60); } .seconds-container { animation: rotate 60s infinite steps(60); }

Teraz wskazówki minut i sekund obracają się w 60 krokach. Przeglądarka automatycznie oblicza o ile każdy z tych 60 kroków ma się przesunąć.

Prawidłowy czas

Dobrze jest mieć ładny zegar, ale co z wskazywaniem właściwego czasu? Z pomocą kodu JavaScript możemy ustawić prawidłowy czas dla naszych odwiedzających. Oto kod.

/* * Starts any clocks using the user’s local time * From: cssanimation.rocks/clocks */ function initLocalClocks() { // Get the local time using JS var date = new Date; var seconds = date.getSeconds(); var minutes = date.getMinutes(); var hours = date.getHours();

// Create an object with each hand and it’s angle in degrees var hands = [ { hand: ‘hours’, angle: (hours * 30) + (minutes / 2) }, { hand: ‘minutes’, angle: (minutes * 6) }, { hand: ‘seconds’, angle: (seconds * 6) } ]; // Loop through each of these hands to set their angle for (var j = 0; j < hands.length; j++) { var elements = document.querySelectorAll(‘.’ + hands[j].hand); for (var k = 0; k < elements.length; k++) { elements[k].style.webkitTransform = ‘rotateZ(‘+ hands[j].angle +’deg)’; elements[k].style.transform = ‘rotateZ(‘+ hands[j].angle +’deg)’; // If this is a minute hand, note the seconds position (to calculate minute position later) if (hands[j].hand === ‘minutes’) { elements[k].parentNode.setAttribute(‘data-second-angle’, hands[j + 1].angle); } } } }

Ta funkcja konwertuje czas (godziny, minuty i sekundy) do odpowiedniego kąta każdej wskazówki. Następnie zapętla każdą wskazówkę i stosuje kąt korzystając z style.transform właściwości rotateZ.

To zadziała na większości przeglądarek z wyjątkiem Safari i innych wymagających prefiksu. Aby to umożliwić użyjemy również właściwość style.webkitTransform.

To następnie synchronizuje zegar z czasem systemu.

Wyrównywanie wskazówek sekund i minut.

Musimy się upewnić, że wskazówka minut przesunie się w momencie, gdy wskazówka sekund będzie wskazywała godzinę dwunastą.

Minute hand moving when second hand hits 12

Kiedy zegar jest po raz pierwszy rysowany na ekranie, minie mniej niż jedna minuta zanim wskazówka musi się przesunąć.  Aby to umożliwić, możemy obliczyć jak długo trwa pierwsza minuta i ręcznie popchnąć wskazówkę. Ponieważ korzystamy z JavaScript, aby wykonać pierwszy ruch, możemy nadal obracać wskazówki o sześć stopni co minutę za pomocą setInterval.

Przed przesunięciem wskazówki minut, musimy zakomunikować ile trwa bieżąca minuta. Być może zauważyłeś te wiersze.

if (degrees[j].hand === ‘minutes’) { elements[k].parentNode.setAttribute(‘data-second-angle’, degrees[j + 1].degree); }

Te dodatkowe wiersze sprawdzają czy wskazówka jest "minutowa" i jeśli jest, to zestawia atrybut danych z bieżącym kątem wskazówki sekund.

Po ustawieniu atrybutu danych, możemy użyć ich do określenia momentu przesunięcia wskazówki minutowej.

/* * Set a timeout for the first minute hand movement (less than 1 minute), then rotate it every minute after that */ function setUpMinuteHands() { // Find out how far into the minute we are var containers = document.querySelectorAll(‘.minutes-container’); var secondAngle = containers[0].getAttribute(“data-second-angle”); if (secondAngle > 0) { // Set a timeout until the end of the current minute, to move the hand var delay = (((360 - secondAngle) / 6) + 0.1) * 1000; setTimeout(function() { moveMinuteHands(containers); }, delay); } }

/* * Do the first minute’s rotation */ function moveMinuteHands(containers) { for (var i = 0; i < containers.length; i++) { containers[i].style.webkitTransform = ‘rotateZ(6deg)’; containers[i].style.transform = ‘rotateZ(6deg)’; } // Then continue with a 60 second interval setInterval(function() { for (var i = 0; i < containers.length; i++) { if (containers[i].angle === undefined) { containers[i].angle = 12; } else { containers[i].angle += 6; } containers[i].style.webkitTransform = ‘rotateZ(‘+ containers[i].angle +’deg)’; containers[i].style.transform = ‘rotateZ(‘+ containers[i].angle +’deg)’; } }, 60000); }

Dodawanie odbicia

Ponieważ korzystamy z JavaScript do poruszania wskazówki minut, powinniśmy usunąć właściwość animacja. Zamiast usuwać, możemy zastąpić ją przejściem. To okazja, aby dodać nieco więcej charakteru do animacji.

Kiedy JavaScript ustawia nowy kąt dla wskazówki, przejście CSS na elemencie przekaże przeglądarce, aby animować nową pozycję. To oznacza, że JavaScript odnosi się tylko do prostych zmian kąta i przeglądarka może zająć się animowaniem ich.

Zanim to zrobimy, powinniśmy zaktualizować kod, aby użyć JavaScript również do poruszania wskazówką sekund. Użyjmy tego kodu, aby animować kontenery wskazówki sekund sześćdziesiąt razy na minutę.

/* * Move the second containers */ function moveSecondHands() { var containers = document.querySelectorAll(‘.seconds-container’); setInterval(function() { for (var i = 0; i < containers.length; i++) { if (containers[i].angle === undefined) { containers[i].angle = 6; } else { containers[i].angle += 6; } containers[i].style.webkitTransform = ‘rotateZ(‘+ containers[i].angle +’deg)’; containers[i].style.transform = ‘rotateZ(‘+ containers[i].angle +’deg)’; } }, 1000); }

Po skonfigurowaniu obsługiwania wskazówek minut i sekund przez JavaScript, zaktualizuj CSS zastępując animation właściwościami transition.

.minutes-container { transition: transform 0.3s cubic-bezier(.4,2.08,.55,.44); } .seconds-container { transition: transform 0.2s cubic-bezier(.4,2.08,.55,.44); }

Te przejścia dotyczą właściwości transform i korzystają z funkcji czasowej cubic-bezier. Funkcja czasowa nadaje wskazówkom efekt odbicia.

Styl iOS 7

Jestem wielkim fanem prostoty zegara znajdującego się w Apple iOS 7. Od tego czasu wydłużyli wskazówki, ale ja wolę wersję z krótszymi.

Stylizując wskazówki i dodając obrazek tła z liczbami, możemy z łatwością osiągnąć ten wygląd.

Projekt jest ewolucją Zegara Swiss Railway od Hansa Hilfikera. Zmieniając kilka rzeczy i dodając nowy obrazek tła, możemy przystosować nasz zegar do tego stylu.

Jeśli opracujesz inne projekty, daj mi znać.

Używanie Moment.js

Jednym z moich pierwszych pomysłów podczas planowania tego artykułu było odtworzenie sceny z trzema zegarami hotelowymi pokazującymi różne strefy czasowe.

Najprostszym sposobem na ustawienie różnych stref czasowych jest użycie wspaniałej biblioteki Moment.js Timezone.

Nowy Jork
Londyn
Tokio

Możesz zobaczyć kod w akcji na Codepen.

Kompatybilność z przeglądarkami

Nowoczesne przeglądarki są w stanie obsłużyć przejścia i animacje CSS związane z zegarem. IE10+ oraz najnowsze przeglądarki Chrome i Firefox obsługują je bez prefiksów, a Safari wymaga prefiksu -webkit.

Nie zapomnij dodać prefiksów do właściwości w kodzie JavaScript.