Analógové (ručičkové) hodiny v JavaScript-e
Nasledujúci článok sa bude venovať vytvoreniu analógových (ručičkových) hodín pomocou JavaScript-u.
Ako už názov napovedá, náplňou tohto článku bude vytvorenie analógových hodín pomocou JavaScript-u.
Dalo by sa povedať, že sa jedná o voľné pokračovanie článku o digitálnych hodinách (> link <), ktorý vyšiel pár mesiacov dozadu.Na prvý pohľad by sa mohlo zdať, že vytvoriť analógové hodiny musí byť omnoho zložitejšie ako digitálne (kde sa technicky jedná len o skladanie vizuálnej hodnoty pomocou príslušných obrázkov číslic), no nie je to celkom tak. Celkový výkonný kód sa vojde do 130 riadkov.Celý princíp fungovania bude založený na canvas-e, funkciách translate, rotate, drawImage a obrázkoch, ktoré budú reprezentovať jednotlivé časti hodín (hodinová, minútová a sekundová ručička + čiary ktoré rozdeľujú ciferník na 12 častí).
var clock =
{
//Pocet poloziek, ktore sa este nacitavaju
ItemsToLoad: 0, //Cache cifernika
lastMinute: -1,
lastMinuteData: null, //Obrazky
img_line: null,
img_second: null,
img_minute: null,
img_hour: null, //Polomer
Radius: 0, //Kresliaca plocha
Canvas: null,
Context: null,
ContextWidth: 0,
ContextHeight: 0,
..
}
O význame jednotlivých premenných napovedá ich názov, avšak pozastavím sa pri dvojici označenej ako //Cache cifernika. Keďže hodiny budú mať aj sekundovú ručičku, budeme ich musieť prekresľovať každú sekundu. Aby sa však nemuseli zakaždým prekresľovať celé hodiny, budeme si ukladať aktuálne nakreslený stav hodín bez sekundovej ručičky do premennej lastMinuteData a sekundovú ručičku dokreslíme dodatočne. Pri každom kreslení si overíme, či sme sa od posledného kreslenia neposunuli na ďalšiu minútu (predchádzajúca hodnota bude uložená v premennej lastMinute), ak nie, obnovíme stav kresliacej plochy z premennej lastMinuteData a dokreslíme sekundovú ručičku. Ak sme už prešli na nasledujúcu minútu, prekreslíme celé hodiny nanovo a uložíme si aktuálny stav (samozrejme bez sekúnd).Pokračujeme zadefinovaním trojice funkcií ktoré som použil aj predchádzajúcich JavaScript-ových projektoch a to CreateImage, ImageLoaded a CheckLoadedFiles. Slúžia na načítanie obrázkov a následné spustenie výkonného kódu. (bližšie boli popísané tu: > link <)
Load: function ()
{
//Inicializacia kresliaceho prostredia
this.Canvas = document.getElementById('clock');
this.Context = this.Canvas.getContext("2d"); // Odpamatame si rozmery kresliacej plochy
this.ContextWidth = this.Canvas.offsetWidth;
this.ContextHeight = this.Canvas.offsetHeight; //Vyratame polomer cifernika
this.Radius = this.ContextWidth / 2; //Nacitame obrazky
this.img_line = this.CreateImage("line");
this.img_second = this.CreateImage("second");
this.img_minute = this.CreateImage("minute");
this.img_hour = this.CreateImage("hour");
},ShowClock: function ()
{
alert("Vsetko bolo nacitane")
}
V úvode nového kódu máme inicializáciu premennej Canvas, ktorá bude obsahovať referenciu na element canvas na našej stránke a premennej Context cez ktorú budeme kresliť. Nasleduje odpamätanie si rozmerov kresliacej plochy (ContextWidth a ContextHeight) a vyrátanie polomeru "ciferníka" hodín do premennej Radius. Hodiny sa budú kresliť po celej ploche canvas-u, čiže polomer nám bude slúžiť na určenie stredu ciferníka. Nasleduje kód na načítanie jednotlivých súčastí (obrázkov) hodín, kde img_line bude reprezentovať čiarky znázorňujúce jednotlivé časti ciferníka (hodiny, resp 5 minútové bloky) a ostatné budú obsahovať obrázky ručičiek pre hodiny (img_hour), minúty (img_minute) a sekundy (img_second).Projekt uložíme, spustíme v prehliadači a malo by sa nám zobraziť hlásenie "Vsetko bolo nacitane". Ak je všetko ok, môžeme pokračovať.Nasleduje zadefinovanie hlavnej kresliacej funkcie našeho projektu DrawElem.
Práve táto funkcia je akýmsi srdcom tohto riešenia. Vstupnými parametrami sú referencia na objekt obrázku, rotácia v stupňoch a pozícia na ktorú chceme obrázok nakresliť. Na začiatku vyrátame aktuálnu hodnotu v radiánoch (vyžaduje ju metóda rotate) a pomocou funkcií translate a rotate zrotujeme kresliacu plochu. Funkcia translate slúži na premapovanie "nultých súradníc" (0, 0) na novú pozíciu. Práve "nulté súradnice" sú kľúčové pre funkciu rotate, ktorá rotuje kresliacu plochu o určenú hodnotu, pričom stredom pre rotáciu je práve "nultá pozícia". Čiže keby sme ju nezmenili, rotate by nerotovala samotnú kresliacu plochu, ale by sme dosiahli efekt, ako by kresliaca plocha obiehala nultý bod (vid. obrázok).Takúto rotáciu vykonáme 2x. Najskôr zrotujeme kresliacu plochu na požadovanú hodnotu, následne nakreslíme požadovaný obrázok a nakoniec kresliacu plochu zrotujeme a pôvodnú hodnotu.Pokračujeme vytvorením funkcie DrawHand, ktorá bude slúžiť na kreslenie jednotlivých ručičiek hodín.
Diskusia
Dalo by sa povedať, že sa jedná o voľné pokračovanie článku o digitálnych hodinách (> link <), ktorý vyšiel pár mesiacov dozadu.Na prvý pohľad by sa mohlo zdať, že vytvoriť analógové hodiny musí byť omnoho zložitejšie ako digitálne (kde sa technicky jedná len o skladanie vizuálnej hodnoty pomocou príslušných obrázkov číslic), no nie je to celkom tak. Celkový výkonný kód sa vojde do 130 riadkov.Celý princíp fungovania bude založený na canvas-e, funkciách translate, rotate, drawImage a obrázkoch, ktoré budú reprezentovať jednotlivé časti hodín (hodinová, minútová a sekundová ručička + čiary ktoré rozdeľujú ciferník na 12 častí).
Základná konštrukcia
Na úvod začneme základnou konštrukciou.Vytvoríme nový priečinok pre projekt a v ňom 3 podpriečinky: css, imgs a js. V priečinku css vytvoríme súbor main.css, v priečinku js súbor clock.js, do priečinka imgs rozbalíme súbory z nasledujúcej linky > link < a samozrejme v priečinku projektu vytvoríme aj súbor index.html.Do súboru index.html vložíme nasledujúci kód:Zdrojový kód:
<!DOCTYPE html>
<html lang="sk">
<head>
<meta charset="utf-8">
<title>Clock</title>
<link type="text/css" rel="StyleSheet" href="css/main.css" />
<script type="text/javascript" src="js/clock.js"></script>
</head>
<body onload="clock.Load();">
<div id="centerdiv">
<canvas id="clock" width="150" height="150"></canvas>
</div>
</body>
</html>
Súbor štýlov main.css bude obsahovať:<html lang="sk">
<head>
<meta charset="utf-8">
<title>Clock</title>
<link type="text/css" rel="StyleSheet" href="css/main.css" />
<script type="text/javascript" src="js/clock.js"></script>
</head>
<body onload="clock.Load();">
<div id="centerdiv">
<canvas id="clock" width="150" height="150"></canvas>
</div>
</body>
</html>
Zdrojový kód:
html, body
{
background:white;
}
#centerdiv
{
height:100%;
display:flex;
justify-content: center;
align-items: center;
}
A nakoniec vložíme prvotný kód aj do clock.js:{
background:white;
}
#centerdiv
{
height:100%;
display:flex;
justify-content: center;
align-items: center;
}
Zdrojový kód:
var clock =
{
Load: function ()
{
alert("ok");
},
};
Vytvorili sme si základnú html stránku v ktorej máme canvas o rozmeroch 150 x 150 px, zarovnaný na stred a nalinkované ostatné súbory projektu. Stránka obsahuje aj volanie funkcie Load objektu clock. Projekt uložíme a súbor index.html môžeme otestovať v prehliadači (stránka zatiaľ zobrazí len hlásenie OK).{
Load: function ()
{
alert("ok");
},
};
Výkonný kód
Na začiatok objektu si vložíme všetky premenné, ktoré budeme potrebovať.Zdrojový kód:
var clock =
{
//Pocet poloziek, ktore sa este nacitavaju
ItemsToLoad: 0, //Cache cifernika
lastMinute: -1,
lastMinuteData: null, //Obrazky
img_line: null,
img_second: null,
img_minute: null,
img_hour: null, //Polomer
Radius: 0, //Kresliaca plocha
Canvas: null,
Context: null,
ContextWidth: 0,
ContextHeight: 0,
..
}
Zdrojový kód:
CreateImage: function (FileName)
{
var img = new Image();
this.ItemsToLoad++;
img.src = "imgs/" + FileName + ".png";
img.onload = this.ImageLoaded;
return img;
},
ImageLoaded: function ()
{
this.onload = null;
clock.ItemsToLoad--;
clock.CheckLoadedFiles();
},
CheckLoadedFiles: function ()
{
if (this.ItemsToLoad <= 0)
{
this.ShowClock();
}
},
Kód CheckLoadedFiles obsahuje volanie funkcie ShowClock ktorú si definujeme spolu z novým kódom Load{
var img = new Image();
this.ItemsToLoad++;
img.src = "imgs/" + FileName + ".png";
img.onload = this.ImageLoaded;
return img;
},
ImageLoaded: function ()
{
this.onload = null;
clock.ItemsToLoad--;
clock.CheckLoadedFiles();
},
CheckLoadedFiles: function ()
{
if (this.ItemsToLoad <= 0)
{
this.ShowClock();
}
},
Zdrojový kód:
Load: function ()
{
//Inicializacia kresliaceho prostredia
this.Canvas = document.getElementById('clock');
this.Context = this.Canvas.getContext("2d"); // Odpamatame si rozmery kresliacej plochy
this.ContextWidth = this.Canvas.offsetWidth;
this.ContextHeight = this.Canvas.offsetHeight; //Vyratame polomer cifernika
this.Radius = this.ContextWidth / 2; //Nacitame obrazky
this.img_line = this.CreateImage("line");
this.img_second = this.CreateImage("second");
this.img_minute = this.CreateImage("minute");
this.img_hour = this.CreateImage("hour");
},ShowClock: function ()
{
alert("Vsetko bolo nacitane")
}
Polomer ciferníka
V úvode nového kódu máme inicializáciu premennej Canvas, ktorá bude obsahovať referenciu na element canvas na našej stránke a premennej Context cez ktorú budeme kresliť. Nasleduje odpamätanie si rozmerov kresliacej plochy (ContextWidth a ContextHeight) a vyrátanie polomeru "ciferníka" hodín do premennej Radius. Hodiny sa budú kresliť po celej ploche canvas-u, čiže polomer nám bude slúžiť na určenie stredu ciferníka. Nasleduje kód na načítanie jednotlivých súčastí (obrázkov) hodín, kde img_line bude reprezentovať čiarky znázorňujúce jednotlivé časti ciferníka (hodiny, resp 5 minútové bloky) a ostatné budú obsahovať obrázky ručičiek pre hodiny (img_hour), minúty (img_minute) a sekundy (img_second).Projekt uložíme, spustíme v prehliadači a malo by sa nám zobraziť hlásenie "Vsetko bolo nacitane". Ak je všetko ok, môžeme pokračovať.Nasleduje zadefinovanie hlavnej kresliacej funkcie našeho projektu DrawElem.
Zdrojový kód:
DrawElem: function (img, degrees, x, y)
{
var Radiants = degrees * Math.PI / 180; this.Context.translate(this.Radius, this.Radius);
this.Context.rotate(Radiants);
this.Context.translate(-this.Radius, -this.Radius); this.Context.drawImage(img, x, y); this.Context.translate(this.Radius, this.Radius);
this.Context.rotate(-Radiants);
this.Context.translate(-this.Radius, -this.Radius);
}
{
var Radiants = degrees * Math.PI / 180; this.Context.translate(this.Radius, this.Radius);
this.Context.rotate(Radiants);
this.Context.translate(-this.Radius, -this.Radius); this.Context.drawImage(img, x, y); this.Context.translate(this.Radius, this.Radius);
this.Context.rotate(-Radiants);
this.Context.translate(-this.Radius, -this.Radius);
}
Rotate bez a s použitím translate
Zdrojový kód:
DrawHand: function (img, degrees)
{
var imgHeight = img.naturalHeight;
var imgWidth = img.naturalWidth; var x = this.Radius - (imgWidth / 2);
var y = 3 + this.Radius - imgHeight;
this.DrawElem(img, degrees, x, y);
},
Funkcia vo svojich parametroch prijíma referenciu na objekt obrázku na vykreslenie a rotáciu v stupňoch. Najskôr zistíme rozmery obrázka, následne vyrátame jeho pozíciu a pomocou DrawElem vykreslíme.Všetko už máme pripravené, pridáme kód do ShowClock.{
var imgHeight = img.naturalHeight;
var imgWidth = img.naturalWidth; var x = this.Radius - (imgWidth / 2);
var y = 3 + this.Radius - imgHeight;
this.DrawElem(img, degrees, x, y);
},
Zdrojový kód:
ShowClock: function ()
{
this.Context.clearRect(0, 0, this.ContextWidth, this.ContextHeight); var actDate = new Date(); var Minutes = actDate.getMinutes();
var Hours = actDate.getHours(); if (Hours >= 12)
Hours -= 12; if (this.lastMinute !== Minutes)
{
for (var i = 0; i < 12; i++)
{
this.DrawElem(this.img_line, i * 30.0, this.Radius, 5);
} var TotalHours = Hours + Minutes / 60.0;
this.DrawHand(this.img_hour, Math.round(360.0 / 12.0 * TotalHours, 2)); this.DrawHand(this.img_minute, 360.0 / 60.0 * Minutes); //Ulozime obrazok do cache
this.lastMinute = Minutes;
this.lastMinuteData = this.Context.getImageData(0, 0, this.ContextWidth, this.ContextHeight);
}
else
{ //Nacitame obrazku z cache
this.Context.putImageData(this.lastMinuteData, 0, 0);
} var Seconds = actDate.getSeconds();
this.DrawHand(this.img_second, 360.0 / 60.0 * Seconds);
window.setTimeout("clock.ShowClock();", 1000);
},
Najskôr vymažeme kresliacu plochu pomocou clearRect a zistíme jednotlivé hodnoty aktuálneho času. V prípade, ak je aktuálna hodina vyššia ako 12, znížime ju o 12, aby ju bolo možné na ciferníku zobraziť.Následne sa pozrieme, či sa od posledného prekreslenia sa zmenila hodnota minúty, ak áno prekreslíme uložený stav, inak obnovíme uložený stav a následne dokreslíme sekundovú ručičku. Pri kreslení je sekundovej a minútovej ručičky použijeme rovnaký vzorec na určenie jej pozície (resp. uhla): 360 / 60 * [Hodnota]. V prípade hodinovej ručičky to bude trochu zložitejšie. Keby použijeme len obdobný vzorec pre hodinovú ručičku, bola by jej pozícia mätúca. Preto budeme rátať nie len s celou hodinou, ale aj s časťou, ktorú nám tvoria minúty. Tento údaj vyrátame v premennej TotalHours.Nakoniec zavoláme window.setTimeout, pomocou ktorej zaistíme opätovné zavolanie funkcie ShowClock o ďalšiu sekundu.Týmto sme naše hodiny dokončili a projekt môžeme uložiť a vyskúšať v prehliadači.Kompletné zdrojové kódy tohto projektu môžete sťahovať z: > link <{
this.Context.clearRect(0, 0, this.ContextWidth, this.ContextHeight); var actDate = new Date(); var Minutes = actDate.getMinutes();
var Hours = actDate.getHours(); if (Hours >= 12)
Hours -= 12; if (this.lastMinute !== Minutes)
{
for (var i = 0; i < 12; i++)
{
this.DrawElem(this.img_line, i * 30.0, this.Radius, 5);
} var TotalHours = Hours + Minutes / 60.0;
this.DrawHand(this.img_hour, Math.round(360.0 / 12.0 * TotalHours, 2)); this.DrawHand(this.img_minute, 360.0 / 60.0 * Minutes); //Ulozime obrazok do cache
this.lastMinute = Minutes;
this.lastMinuteData = this.Context.getImageData(0, 0, this.ContextWidth, this.ContextHeight);
}
else
{ //Nacitame obrazku z cache
this.Context.putImageData(this.lastMinuteData, 0, 0);
} var Seconds = actDate.getSeconds();
this.DrawHand(this.img_second, 360.0 / 60.0 * Seconds);
window.setTimeout("clock.ShowClock();", 1000);
},
Žiadne príspevky v diskusii.
Na prispievanie do diskusie musíte byť prihlásený.