IT rekvalifikácia. Seniorní programátori zarábajú až 6 000 €/mesiac a rekvalifikácia je prvým krokom. Zisti, ako na to!

28. diel - Časovače a animácie v JavaScripte

V predchádzajúcom cvičení, Riešené úlohy k 24.-27. lekciu JavaScriptu, sme si precvičili získané skúsenosti z predchádzajúcich lekcií.

V tomto tutoriále základov JavaScriptu si ukážeme, ako našim kresbám nastaviť časovače a docieliť tým efekt animácie na webovej stránke.

Časovače a animácie

Časovače môžeme v JavaScripte obsluhovať pomocou dvoch funkcií, ktoré si teraz predstavíme.

Funkcia setInterval() a setTimeout()

Funkcia setInterval() a setTimeout() prijímajú dva parametre. Volanú funkciu (bez zátvoriek) a časový interval určujúci, ako často alebo za ako dlho sa bude táto funkcia volať. Časové intervaly sa udávajú v milisekundách. Jedna sekunda je teda tisíc milisekúnd. Rozdiel medzi týmito dvoma funkciami je vcelku zásadný. Zatiaľ čo funkcia setInterval() bude funkciu volať každých x milisekúnd, funkcia setTimeout() ju zavolá iba raz, a to za x milisekúnd. Preto funkciu setTimeout() použijeme, ak chceme nastaviť odklad nejakej akcii a funkciu setInterval() využijeme, keď potrebujeme nastaviť pravidelné opakovanie určitej akcie.

Postupné vypísanie textu

Ukážme si použitie časovača najskôr na jednoduchom príklade. Budeme v ňom postupne vypisovať text Hello World. Najprv bude vidieť písmeno H, o sekundu neskôr priskočí e a takto sa budú pridávať ďalšie písmená. Akonáhle bude vypísaný celý text, zmaže sa a začne to celé znova.

V tele HTML nebudeme mať žiadny element, všetko vytvoríme až z JavaScriptu:

<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="UTF-8" />
        <title>Text animation</title>
        <script src="text_animation.js"></script>
    </head>
    <body>
    </body>
</html>

V súbore text_animation.js vytvoríme premennú text, kam uložíme náš výpis Hello World. Potom vytvoríme element <p>, počkáme na načítanie stránky a pridáme ho do tela dokumentu:

let text = "Hello World";
let paragraph = document.createElement("p");

window.onload = function() {
    document.body.appendChild(paragraph);
}

Funkcia changeText()

Teraz vytvoríme funkciu, ktorá bude meniť text v našom elemente. Pridáme do nej podmienku, ktorá overí, či už nemáme vypísaný celý text. Ak áno, celý obsah odseku vymažeme a budeme pokračovať od začiatku:

function changeText() {

    if (paragraph.textContent == text) {
        paragraph.textContent = "";
    } else {
        // We'll add some more code here
    }
}

Uvedená podmienka je pre správnu funkčnosť ukážky nevyhnutná. Ak by sme skúsili na konci textu získať ďalšie písmeno, dostali by sme chybu, pretože takéto písmeno v našej premennej text neexistuje.

Vo vetve else doplníme ďalší kód na zaistenie postupného výpisu textu. Vezmeme písmeno (znak) z premennej text, ktoré pridáme k existujúcemu textu elementu paragraph. Už vieme, že vlastnosť length obsahuje dĺžku reťazca. Ďalší znak na vypísanie získame tak, že zmeriame dĺžku reťazca, ktorý je vypísaný na obrazovku a použijeme ho ako index znaku premennej text. Pomocou operátora += ho potom vložíme do elementu paragraph.

Dĺžka reťazca sa počíta od 1, pre text s jedným znakom nám vráti vlastnosť length hodnotu 1. Znak na určitej pozícii textu získavame pomocou indexu, ktorý sa počíta od nuly. Prvý znak má teda index 0 a tak ďalej. Preto znak na indexe aktuálnej dĺžky textu vypísaného na obrazovke reprezentuje nasledujúci znak na vypísanie.

Nakoniec pomocou ďalšej podmienky overíme, či nevypisujeme medzeru. Ak áno, zavoláme funkciu changeText() a vypíšeme ihneď ďalší znak, aby používateľ nemal pocit, že sa aplikácia zasekla:

let letterToType = text[paragraph.textContent.length];
paragraph.textContent += letterToType;

if (letterToType == " ") {
    chageText();
}

Nastavenie časovača

Teraz zostáva iba spustiť interval. Do obsluhy udalosti onload pridáme funkciu setInterval(), ktoré ako prvý parameter odovzdáme funkciu changeText() (bez zátvoriek) a ako druhý parameter dosadíme číslo 1000, čím nastavíme interval zmeny textu na jednu sekundu. Interval sa spustí až po jednej sekunde. Dovtedy to bude vyzerať, že aplikácia vôbec nereaguje, preto predtým ešte sami zavoláme funkciu changeText():

changeText();
setInterval(changeText, 1000);

Celý kód vyzerá takto:

let text = "Hello World";
let paragraph = document.createElement("p");

window.onload = function() {
    document.body.appendChild(paragraph);
    changeText();
    setInterval(changeText, 1000);
}

function changeText() {
    if (paragraph.textContent == text) {
        paragraph.textContent = "";
    } else {
        let letterToType = text[paragraph.textContent.length];
        paragraph.textContent += letterToType;

        if (letterToType == " ") {
            changeText();
        }
    }
}

Výsledok v prehliadači:

Text animation
localhost

Zložitejšie animácie

Bolo by pekné mať na webe aj nejakú zložitejšiu animáciu. Jednoduché animácie je možné vyriešiť v CSS, ale u tých zložitejších už musíme použiť JavaScript. Celá pointa animácií spočíva v tom, že v nejakom intervale ovplyvňujeme vlastnosti animovaného objektu.

Vezmeme si napríklad jesennú výzdobu webu. Naprogramujeme skript, ktorý nechá padať lístie zhora nadol. Každý obrázok bude mať atribút data-autumn. Cielene budeme vyberať iba tieto obrázky, pretože na webe môžu byť (a bývajú) aj iné obrázky a tie nechceme ovplyvňovať.

Stiahneme si obrázok listu nižšie a vložíme ho do nového projektu:

List - Základné konštrukcie jazyka JavaScript

V HTML súbore doplníme opäť iba hlavičku a telo necháme prázdne:

<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="UTF-8" />
        <title>Autumn decoration</title>
        <script src="leaves.js"></script>
        <link href="style.css" rel="stylesheet" />
    </head>
    <body>
    </body>
</html>

Do projektu si pridáme CSS súbor, v ktorom nastavíme obrázkom absolútnu pozíciu a <body> naštylujeme tak, aby nezobrazovalo scrollbary:

body > img[data-autumn] {
    position: absolute;
}

body {
    overflow: hidden;
}

V súbore leaves.js v udalosti onload najprv vytvoríme elementy listov s atribútom data-autumn a vložíme ich do tela HTML:

document.addEventListener("DOMContentLoaded", function() {
    for (let i = 0; i < 5; i++) {
        let img = document.createElement("img");
        img.src = "leaf.jpg";
        img.setAttribute("data-autumn", "");
        img.alt = "Leaf";
        document.body.appendChild(img);
    }

    // We will set the initial position of the leaves

});

Na nastavenie udalosti onload sme tentoraz použili nám už známu metódu addEventListener().

Zistenie veľkosti okna

Listom teraz budeme chcieť nastaviť východiskovú pozíciu, ktorú vypočítame zľava ako pätinu šírky okna pre každý list. Hornú pozíciu vypočítame ako výšku okna mínus výška listu, aby pri načítaní stránky začali schádzať z hornej hrany. Pretože potrebujeme pracovať s veľkosťou okna, ukážeme si niekoľko vlastností, ktoré s tým súvisia.

Veľkosť obrazovky

Veľkosť obrazovky zistíme pomocou vlastnosti width a height na objekte screen. Ak chceme zistiť veľkosť obrazovky bez systémových panelov (napr. taskbaru), použijeme vlastnosti availWidth a availHeight na objekte screen.

Veľkosť okna webovej stránky

My však potrebujeme zistiť veľkosť plochy, ktorú môže zaberať naša aplikácia. Vlastnosti okna webovej stránky nájdeme na objekte window. Šírku reprezentuje vlastnosť innerWidth, výšku reprezentuje vlastnosť innerHeight.

Nastavenie CSS vlastností z JavaScriptu

Ešte nám chýba jedna podstatná informácia o tom, ako sa nastavujú CSS vlastnosti elementom z JavaScriptu. Všetky elementy DOM majú vlastnosť style, ktorá obsahuje vlastnosti pomenované ako CSS vlastnosti. Tie sa nezapisujú pomlčkovú, ale camelCase notácií.

Farbu pozadia elementu <body> na červenú teda nastavíme takto:

document.body.style.backgroundColor = "red";

Nastavenie počiatočnej pozície listov

Vráťme sa k nášmu padajúcemu lístiu a nastavme mu počiatočné pozície. Nezabudneme na doplnenie jednotky px. Namiesto komentára v kóde vyššie doplníme:

let leaves = document.querySelectorAll("body > img[data-autumn]");
let index = 0;

for (let leaf of leaves) {
    leaf.style.left = index * window.innerWidth / leaves.length + "px";
    leaf.style.top = -img.height + "px";
    index++;
}

// We will add the setInterval() function here later

V tomto kóde najskôr získame jednotlivé listy a vytvoríme premennú index s hodnotou 0. V cykle potom nastavíme postupne každému listu pomocou CSS rovnakú hornú pozíciu style.top a ľavý okraj obrázku style.left posúvame násobkom premennej index, ktorú postupne inkrementujeme.

Funkcia shift()

Teraz vytvoríme funkciu shift(), ktorá bude posúvať všetky listy dole. Funkcia opäť cyklom prejde všetky listy a nastaví im novú pozíciu. Tú získa na základe súčasnej pozície listu, ktorú musíme kvôli odstráneniu jednotky z CSS vlastnosti naparzovať. Následne pripočítame nejakú rozumnú hodnotu, aby animácia nebola ani veľmi rýchla ani veľmi pomalá:

function shift(leaves) {
    for (let leaf of leaves) {
        let newPosition = parseInt(leaf.style.top) + 2;
        if (newPosition > window.innerHeight) {
            newPosition = -leaf.height;
        }
        leaf.style.top = newPosition + "px";
    }
}

Nezabudli sme ošetriť prípad, keď list vyjde z okna. V podmienke teda nastavíme novú pozíciu na mínus výšku obrázku.

Teraz zostáva iba doplniť v obsluhe udalosti načítanie okna nastavenia časovača, pridáme teda posledný riadok:

setInterval(() => shift(leaves), 20);

Aplikáciu spustíme. Uvidíme, že lístie bude padať zhora nadol a po vytečení z obrazovky zase znova:

Falling leaves
localhost

Animácie na plátne

V podobnom duchu sa nesú animácie na plátne, kde v určitom intervale celé plátno vymažeme a znovu vykreslíme, a tak stále dookola. Ako ukážku si naprogramujeme koleso šťastia. Pre jednoduchosť si ho načítame zo statického obrázku. Vytvorme si teda stránku s obrázkom a plátnom, ktoré si potom v JavaScripte načítame.

Stiahneme si obrázok nižšie a vložíme ho do nového projektu:

Wheel of fortune - Základné konštrukcie jazyka JavaScript

Do tela HTML súboru potom vložíme obrázok a plátno:

<img src="wheel.png" id="wheel" />
<canvas id="canvas" width="500" height="500"></canvas>

V skripte si objekty z HTML načítajme. K premenným pridáme ešte premennú, kde budeme mať uložený uhol otočenia nastavený na hodnotu 0. Obrázok nezabudneme zo stránky skryť:

let canvas;
let context;
let image;
let rotation = 0;

document.addEventListener("DOMContentLoaded", function() {
    canvas = document.getElementById("canvas");
    context = canvas.getContext("2d");
    image = document.getElementById("wheel");
    image.style.display = "none";
});

V uvedenom kóde sme pôvodný obrázok pre ilustráciu skryli pomocou CSS vlastnosti style.display. Rovnaký efekt by sme dosiahli aj zavolaním metódy removeChild(image) na rodičovi, teda na document.body.

Funkcia redraw()

Ďalej vytvoríme funkciu redraw(), v ktorej najskôr vymažeme plátno. Potom presunieme a otočíme kontext a znova naň koleso vykreslíme. Nakoniec navýšime hodnotu premennej rotation o jeden stupeň. Vieme, že musíme uviesť hodnotu v radiánoch, keď jeden stupeň zodpovedá (2 * PI) / 360:

function redraw() {
    context.clearRect(0, 0, 500, 500);
    context.save();
    context.translate(250, 250);
    context.rotate(rotation);
    context.drawImage(image, -225, -225);
    context.restore();
    rotation += (2 * Math.PI) / 360;
}

Vo funkcii redraw() sme posunuli plátno na jeho aktuálny stred ([250, 250]). Metódou rotate() potom otáčame plátno okolo tejto pozície. Obrázok má výšku aj šírku 450 pixelov, aby sme ho nakreslili na pôvodné miesto, zadáme ako súradnice zápornú hodnotu polovice jeho veľkosti a šírky.

Spustenie a zastavenie animácie

Teraz sa vrátime do tela metódy addEventListener() a opäť tu najskôr zavoláme funkciu redraw(), aby sme nečakali na uplynutie prvého intervalu. Potom nastavíme opakované volanie vykresľovacej funkcie s krátkym intervalom. Na koniec metódy doplníme teda nasledujúce dva riadky:

redraw();
let interval = setInterval(redraw, 20);

Na rozdiel od predchádzajúceho príkladu tu navyše ukladáme volanie funkcie setInterval() do premennej interval. Vďaka tomu budeme môcť spustenú animáciu ukončiť. Keď totiž užívateľ opustí našu záložku prehliadača, animácia stále beží na pozadí a vyťažuje procesor.

Zastavenie animácie zaistíme funkciou clearInterval(), ktorá berie ako parameter premennú obsahujúcu spustenú animáciu. Pretože chceme animáciu ukončiť pri opustení stránky, zavoláme túto funkciu v obsluhe udalosti beforeunload. Celý kód umiestnime aj do tela metódy addEventListener():

window.addEventListener("beforeunload", function() {
    clearInterval(interval);
});

Výsledok:

Wheel of fortune
localhost

Teraz by sme mali už zvládať základy práce s animáciami vrátane ich ukončenia. Dokázať si to môžeme na cvičenie. Ako zaistiť, aby za nás webový prehliadač spúšťal animáciu, len keď je potreba, si povieme inokedy :)

V nasledujúcom cvičení, Riešené úlohy k 28. lekcii JavaScriptu, si precvičíme nadobudnuté skúsenosti z predchádzajúcich lekcií.


 

Mal si s čímkoľvek problém? Stiahni si vzorovú aplikáciu nižšie a porovnaj ju so svojím projektom, chybu tak ľahko nájdeš.

Stiahnuť

Stiahnutím nasledujúceho súboru súhlasíš s licenčnými podmienkami

Stiahnuté 5x (217.48 kB)
Aplikácia je vrátane zdrojových kódov v jazyku JavaScript

 

Predchádzajúci článok
Riešené úlohy k 24.-27. lekciu JavaScriptu
Všetky články v sekcii
Základné konštrukcie jazyka JavaScript
Preskočiť článok
(neodporúčame)
Riešené úlohy k 28. lekcii JavaScriptu
Článok pre vás napísal Michal Žůrek - misaz
Avatar
Užívateľské hodnotenie:
3 hlasov
Autor se věnuje tvorbě aplikací pro počítače, mobilní telefony, mikroprocesory a tvorbě webových stránek a webových aplikací. Nejraději programuje ve Visual Basicu a TypeScript. Ovládá HTML, CSS, JavaScript, TypeScript, C# a Visual Basic.
Aktivity