Zegar cyfrowy z wykorzystaniem KineticJS


2 stycznia 2014 / Michał Załęcki


Święta, święta i po świętach. Jako, że zrobiłem sobie trochę wolnego od pisania to miałem więcej czasu na przygotowanie czegoś ciekawszego niż zwykle. Mając na uwadze, że wszyscy mamy już jakieś pojęcie o elemencie canvas, gdyż poświęciłem mu mój poprzedni artykuł, wybór padł na zegar cyfrowy, a wykorzystałem do tego KineticJS. KIneticJS to „HTML5 Canvas JavaScript framework”, w praktyce oznacza to tyle, że praca z elementem canvas przestaje być, powiedzmy wprost, tak nudna.

Wszyscy, którzy uczciwie przebrnęli przez mój poprzedni wpis i zobaczyli jak „przyjemna” jest praca z canvas szybko przekonają się do KineticJS. Nie jest to oczywiście jedyne narzędzie w swojej klasie. Bardzo ciekawy jest też jCanvas (szczególnie dla fanów jQuery), jednak miałem obawy o wydajność naszego zegara (sekundnik i milisekundnik), więc możliwość wsadowego przerysowywania warstwy w KineticJS przesądziła o jego wyborze do tego projektu, ale o tym jeszcze później.

HTML

JS

#1

Na początku musimy utworzyć obiekt Stage do którego będziemy dodawać poszczególne warstwy, oraz stworzyć same warstwy, kolejno dla: zegara (rysujemy ją tylko raz), cyfry oznaczające godzinę (przerysowujemy raz na minutę), cyfry oznaczające minuty (przerysowujemy raz na sekundę) i milisekundnik (przerysowywany wsadowo).

#2

Pozostało jeszcze stworzyć obiekt z ustawieniami, tak aby dostosowanie wyglądu i działania zegara było później jak najprostsze.

#3

Prosty prostokąt posłuży nam jako tło dla zegara. Widać na tym prostym przykładzie, że obliczanie poszczególnych współrzędnych jest dość złożone, ale dzięki temu bardzo łatwo przystosować zegar według własnych preferencji, korzystając z obiektu ustawień.

#4

Funkcja drawLed zwraca pojedynczą diodę (wielokąt) o początku w punkcie (peakX, peakY) i pozwala na jej obrót o rotDeg stopni.

#5

Funkcja drawDigit zwraca już pojedynczą cyfrę (zestaw 7 wielokątów), jeszcze bez włączonych odpowiednich diod.

#6

Kolejno definiujemy odpowiednie zmienne, które odpowiadać będą cyfrom zegara i kropkom, które oddzielają godziny od minut, minuty od sekund, sekundy od milisekund.

#7

Przypisanie wszystkich obiektów do jednej warstwy spowodowało by, że niepotrzebnie przerysowywalibyśmy kształty, które się nie zmieniły. By zadbać o przyzwoitą wydajność zegara rozmieścimy wszystkie kształty na odpowiednich warstwach. Po dodaniu elementów do warstw należy dodać same warstwy do obiektu Stage, który wskazuje na konkretną kanwę.

#8

Mamy już warstwy ze wszystkimi potrzebnymi elementami, ale narysowanie zegara w tym momencie da nam taki rezultat:

Czegoś brakuje, prawda?

Funkcja setDigit, na podstawie tablicy ustawień wyświetlacza siedmiosegmentowego i przekazanej wartości, zapala odpowiednie diody na wskazanej cyfrze.

#9

Funkcja setTime wykonuje trzy zadania. Pierwsze, pobiera z obiektu Date aktualny czas i ustala wartość dla każdej z cyfr. Drugie, ustawia już wartości dla konkretnych cyfr. Trzecia, to przerysowywanie określonych warstw gdy zajdzie taka potrzeba. Tu na chwile się zatrzymamy.

Sama metoda draw warstwy powoduje jej natychmiastowe przerysowanie, nie ma w tym nic niezwykłego. Metoda batchDraw jest o wiele bardziej interesująca. Silnik animacji w KineticJS automatycznie ogranicza ilość przerysowań, bez znaczenia ile razy w danym czasie wywołamy metodę batchDraw. Dzięki temu warstwa nie jest przerysowywana częściej niż ilość klatek na sekundę jaka może być wyświetlona.

#10

Na koniec czysta formalność w postaci narysowania zegara i ustawienia odświeżania.

Wydajność

Aby zobrazować ile przerysowań zaoszczędzimy wykorzystując metodę batchDraw posłużę się prostymi obliczeniami:

Jeżeli wciągu 1s, przy częstotliwości odświeżania co 1ms, przerysujemy warstwę 1000 razy. Ludzkie oko nie potrafi dostrzec więcej niż 75-85 klatek na sekundę, a częstotliwość odświeżania przeciętnego monitora ciekłokrystalicznego to 60Hz. Wynika z tego, że nie zauważymy ok. 920-940, czyli 92-94% przerysowań. Robi wrażenie, prawda?

setTime vs requestAnimationFrame

W przypadku ustawienia refreshTime na 0, zamiast setTimeout wykorzystywana będzie metoda requestAnimationFrame. W przypadku np. zwykłego stopera gdzie sami odmierzalibyśmy czas skorzystanie z requestAnimationFrame komplikowałoby trochę kwestię inkrementacji. Z powodu, że za każdym razem tworzymy nowy obiekt Date ten problem nas nie dotyczy. Ile zyskamy na skorzystaniu z requestAnimationFrame? IE11 prawdę Ci powie:

setTimeout(setTime,1)

requestAnimationFrame

setTimeout(setTime,1) – bez rysowania wsadowego

Wbrew pozorom nie miałem w tym czasie włączonego Crysis 3.

requestAnimationFrame – bez rysowania wsadowego

Jak widać na żadnym z tych testów mój komputer nie zszedł poniżej 60FPS, więc nie widziałem różnic, ale powyższe wyniki dają do myślenia i pokazują jak bardzo niewielka zmiana w kodzie rzutuje na jego wydajność.

Podsumowanie

Jak widać za pomocą elementu canvas można stworzyć w prosty sposób coś więcej niż kółko czy kwadrat, a za pomocą np. Three.js i WebGL wejść również w świat grafiki 3D. Zachęcam do eksperymentowania i pochwalenia się swoimi dziełami. Cały kod zegara dostępny jest na moim GitHubie.

Przydatne linki


Tagi:


Dodaj komentarz

Twój adres e-mail nie zostanie opublikowany.