Lekce 26 - Časovače a animace v JavaScriptu
V předešlém cvičení, Řešené úlohy k 23.-25. lekci JavaScriptu, jsme si procvičili nabyté zkušenosti z předchozích lekcí.
V tomto tutoriálu základů JavaScriptu si ukážeme, jak našim kresbám nastavit časovače a docílit tím efektu animace na webové stránce.
Časovače a animace
Časovače můžeme v JavaScriptu obsluhovat pomocí dvou funkcí, které si nyní představíme.
Funkce setInterval()
a
setTimeout()
Funkce setInterval()
a setTimeout()
přijímají
dva parametry. Volanou funkci (bez závorek) a časový
interval určující, jak často nebo za jak dlouho se bude tato funkce
volat. Časové intervaly se udávají v milisekundách. Jedna sekunda je tedy
tisíc milisekund. Rozdíl mezi těmito dvěma funkcemi je vcelku zásadní.
Zatímco funkce setInterval()
bude funkci volat
každých x
milisekund, funkce
setTimeout()
ji zavolá pouze jednou, a to za
x
milisekund. Proto funkci setTimeout()
použijeme,
chceme-li nastavit odklad nějaké akci a funkci
setInterval()
využijeme, když potřebujeme nastavit
pravidelné opakování určité akce.
Postupné vypsání textu
Ukažme si použití časovače nejprve na jednoduchém příkladu. Budeme v
něm postupně vypisovat text Ahoj světe
. Nejprve bude vidět
písmeno A
, o sekundu později přiskočí h
a takto
se budou přidávat další písmena. Jakmile bude vypsán celý text, smaže se
a začne to celé znovu.
V těle HTML nebudeme mít žádný element, vše vytvoříme až z JavaScriptu:
<!DOCTYPE html> <html lang="cs-cz"> <head> <meta charset="UTF-8" /> <title>Animace textu</title> <script src="animace_textu.js"></script> </head> <body> </body> </html>
V souboru animace_textu.js
vytvoříme proměnnou
text
, kam uložíme náš výpis Ahoj světe
. Poté
vytvoříme element <p>
, počkáme na načtení stránky a
přidáme jej do těla dokumentu:
let text = "Ahoj světe"; let paragraph = document.createElement("p"); window.onload = function() { document.body.appendChild(paragraph); }
Funkce zmenText()
Nyní vytvoříme funkci, která bude měnit text v našem elementu. Přidáme do ní podmínku, která ověří, zda již nemáme vypsaný celý text. Pokud ano, celý obsah odstavce vymažeme a budeme pokračovat od začátku.:
function zmenText() { if (paragraph.textContent == text) { paragraph.textContent = ""; } else { // Sem doplníme další kód } }
Uvedená podmínka je pro správnou funkčnost ukázky
nezbytná. Pokud bychom zkusili na konci textu získat další písmeno, dostali
bychom chybu, protože takové písmeno v naší proměnné text
neexistuje.
Ve větvi else
doplníme další kód pro zajištění
postupného výpisu textu. Vezmeme písmeno (znak) z proměnné
text
, které přidáme ke stávajícímu textu elementu
paragraph
. Již víme, že vlastnost length
obsahuje
délku řetězce. Další znak k vypsání získáme tak, že změříme délku
řetězce, který je vypsaný na obrazovku a použijeme jej jako index znaku
proměnné text
. Pomocí operátoru +=
jej pak
vložíme do elementu paragraph
.
Délka řetězce se počítá od 1
, pro text s
jedním znakem nám vrátí vlastnost length
hodnotu
1
. Znak na určité pozici textu získáváme pomocí
indexu, který se počítá od nuly. První
znak má tedy index 0
a tak dále. Proto znak na indexu aktuální
délky textu vypsaného na obrazovce reprezentuje následující znak k
vypsání.
Nakonec pomocí další podmínky ověříme, zda nevypisujeme mezeru. Pokud
ano, zavoláme funkci zmenText()
a vypíšeme ihned další znak,
aby uživatel neměl pocit, že se aplikace zasekla:
let pismenoKpridani = text[paragraph.textContent.length]; paragraph.textContent += pismenoKpridani; if (pismenoKpridani == " ") { zmenText(); }
Nastavení časovače
Nyní zbývá pouze spustit interval. Do obsluhy události
onload
přidáme funkci setInterval()
, které jako
první parametr předáme funkci zmenText()
(bez závorek) a jako
druhý parametr dosadíme číslo 1000
, čímž nastavíme interval
změny textu na jednu sekundu. Interval se spustí až po jedné sekundě. Do
té doby to bude vypadat, že aplikace vůbec nereaguje, proto předtím ještě
sami zavoláme funkci zmenText()
:
zmenText(); setInterval(zmenText, 1000);
Celý kód vypadá takto:
let text = "Ahoj světe"; let paragraph = document.createElement("p"); window.onload = function() { document.body.appendChild(paragraph); zmenText(); setInterval(zmenText, 1000); } function zmenText() { if (paragraph.textContent == text) { paragraph.textContent = ""; } else { let pismenoKpridani = text[paragraph.textContent.length]; paragraph.textContent += pismenoKpridani; if (pismenoKpridani == " ") { zmenText(); } } }
Výsledek v prohlížeči:
Složitější animace
Bylo by hezké mít na webu i nějakou složitější animaci. Jednoduché animace lze vyřešit v CSS, ale u těch složitějších již musíme použít JavaScript. Celá pointa animací spočívá v tom, že v nějakém intervalu ovlivňujeme vlastnosti animovaného objektu.
Vezmeme si například podzimní výzdobu webu. Naprogramujeme skript, který
nechá padat listí odshora dolů. Každý obrázek bude mít atribut
data-podzim
. Cíleně budeme vybírat pouze tyto obrázky, protože
na webu mohou být (a bývají) i jiné obrázky a ty nechceme ovlivňovat.
Stáhneme si obrázek listu níže a vložíme jej do nového projektu:
V HTML souboru doplníme opět pouze hlavičku a tělo necháme prázdné:
<!DOCTYPE html> <html lang="cs-cz"> <head> <meta charset="UTF-8" /> <title>Podzimní výzdoba</title> <script src="listy.js"></script> <link href="styl.css" rel="stylesheet" /> </head> <body> </body> </html>
Do projektu si přidáme CSS soubor, ve kterém nastavíme obrázkům
absolutní pozici a <body>
nastylujme tak, aby nezobrazovalo
scrollbary:
body > img[data-podzim] { position:absolute; } body { overflow:hidden; }
V souboru listy.js
v události onload
nejprve
vytvoříme elementy listů s data atributem data-podzim
a
vložíme je do těla HTML:
document.addEventListener("DOMContentLoaded", function() { for (let i = 0; i < 5; i++) { let img = document.createElement("img"); img.src = "list.jpg"; img.setAttribute("data-podzim", ""); img.alt = "List"; document.body.appendChild(img); } // Zde vzápětí nastavíme počáteční pozici listů });
Pro nastavení události onload
jsme tentokrát použili nám
již známou metodu addEventListener()
.
Zjištění velikosti okna
Listům nyní budeme chtít nastavit výchozí pozici, kterou vypočítáme zleva jako pětinu šířky okna pro každý list. Horní pozici vypočítáme jako výšku okna mínus výška listu, aby při načtení stránky začaly sjíždět z horní hrany. Protože potřebujeme pracovat s velikostí okna, ukážeme si několik vlastností, které s tím souvisejí.
Velikost obrazovky
Velikost obrazovky zjistíme pomocí vlastnosti width
a
height
na objektu screen
. Chceme-li zjistit velikost
obrazovky bez systémových panelů (např. taskbaru),
použijeme vlastnosti availWidth
a availHeight
na
objektu screen
.
Velikost okna webové stránky
My však potřebujeme zjistit velikost plochy, kterou může zabírat naše
aplikace. Vlastnosti okna webové stránky najdeme na objektu
window
. Šířku reprezentuje vlastnost innerWidth
,
výšku reprezentuje vlastnost innerHeight
.
Nastavení CSS vlastností z JavaScriptu
Ještě nám chybí jedna podstatná informace o tom, jak se
nastavují CSS vlastnosti elementům z JavaScriptu. Všechny
DOM elementy mají vlastnost style
, která obsahuje vlastnosti
pojmenované jako CSS vlastnosti. Ty se nezapisují pomlčkovou, ale camelCase
notací.
Barvu pozadí elementu <body>
na červenou tedy nastavíme
takto:
document.body.style.backgroundColor = "red";
Nastavení počáteční pozice listů
Vraťme se k našemu padajícímu listí a nastavme mu počáteční pozice.
Nezapomeneme na doplnění jednotky px
. Namísto komentáře v
kódu výše doplníme:
let listy = document.querySelectorAll("body > img[data-podzim]"); let index = 0; for (let img of listy) { img.style.left = index * window.innerWidth / listy.length + "px"; img.style.top = -img.height + "px"; index++; } // Sem později doplníme funkci setInterval()
V tomto kódu nejprve získáme jednotlivé listy a vytvoříme proměnnou
index
s hodnotou 0
. V cyklu poté nastavíme postupně
každému listu pomocí CSS stejnou horní pozici style.top
a levý
okraj obrázku style.left
posouváme násobkem proměnné
index
, kterou postupně inkrementujeme.
Funkce posun()
Nyní vytvoříme funkci posun()
, která bude posouvat všechny
listy dolů. Funkce opět cyklem projde všechny listy a nastaví jim novou
pozici. Tu získá na základě současné pozice listu, kterou musíme kvůli
odstranění jednotky z CSS vlastnosti naparsovat. Následně přičteme
nějakou rozumnou hodnotu, aby animace nebyla ani moc rychlá ani moc
pomalá:
function posun(listy) { for (let img of listy) { let novaPozice = parseInt(img.style.top) + 2; if (novaPozice > window.innerHeight) { novaPozice = -img.height; } img.style.top = novaPozice + "px"; } }
Nezapomněli jsme ošetřit případ, kdy list vyjede z okna. V podmínce tedy nastavíme novou pozici na mínus výšku obrázku.
Nyní zbývá pouze doplnit v obsluze události načtení okna nastavení časovače, přidáme tedy poslední řádek:
setInterval(() => posun(listy), 20);
Aplikaci spustíme. Uvidíme, že listí bude padat shora dolů a po vytečení z obrazovky zase znovu:
Animace na plátně
V podobném duchu se nesou animace na plátně, kde v určitém intervalu celé plátno vymažeme a znovu vykreslíme, a tak stále dokola. Jako ukázku si naprogramujeme kolo štěstí. Pro jednoduchost si jej načteme ze statického obrázku. Vytvořme si tedy stránku s obrázkem a plátnem, které si poté v JavaScriptu načteme.
Stáhneme si obrázek níže a vložíme jej do nového projektu:
Do těla HTML souboru pak vložíme obrázek a plátno:
<img src="kolo.png" id="kolo" /> <canvas id="platno" width="500" height="500"></canvas>
Ve skriptu si objekty z HTML načtěme. K proměnným přidáme ještě
proměnnou, kde budeme mít uložený úhel otočení nastavený na hodnotu
0
. Obrázek nezapomeneme ze stránky skrýt:
let platno; let kontext; let obrazek; let otoceni = 0; document.addEventListener("DOMContentLoaded", function() { platno = document.getElementById("platno"); kontext = platno.getContext("2d"); obrazek = document.getElementById("kolo"); obrazek.style.display = "none"; });
V uvedeném kódu jsme původní obrázek pro ilustraci skryli
pomocí CSS vlastnosti style.display
. Stejného efektu bychom
dosáhli také zavoláním metody removeChild(obrazek)
na rodiči,
tedy na document.body
.
Funkce prekresli()
Dále vytvoříme funkci prekresli()
, ve které nejprve
vymažeme plátno. Pak přesuneme a otočíme kontext a znovu na něj kolo
vykreslíme. Nakonec navýšíme hodnotu proměnné otoceni
o
jeden stupeň. Víme, že musíme uvést hodnotu v radiánech, kdy jeden stupeň
odpovídá (2 * Pí) / 360
:
function prekresli() { kontext.clearRect(0, 0, 500, 500); kontext.save(); kontext.translate(250, 250); kontext.rotate(otoceni); kontext.drawImage(obrazek, -225, -225); kontext.restore(); otoceni += (2 * Math.PI) / 360; }
Ve funkci prekresli()
jsme posunuli plátno na jeho aktuální
střed ([250, 250]
). Metodou rotate()
pak otáčíme
plátno kolem této pozice. Obrázek má výšku i šířku 450 pixelů, abychom
jej nakreslili na původní místo, zadáme jako souřadnice zápornou hodnotu
poloviny jeho velikosti a šířky.
Spuštění a zastavení animace
Nyní se vrátíme do těla metody addEventListener()
a opět
zde nejprve zavoláme funkci prekresli()
, abychom nečekali na
uplynutí prvního intervalu. Poté nastavíme opakované volání vykreslovací
funkce s krátkým intervalem. Na konec metody doplníme tedy následující dva
řádky:
prekresli(); let interval = setInterval(prekresli, 20);
Na rozdíl od předchozího příkladu zde navíc ukládáme volání funkce
setInterval()
do proměnné interval
. Díky tomu
budeme moci spuštěnou animaci ukončit. Když totiž
uživatel opustí naši záložku prohlížeče, animace stále běží na
pozadí a vytěžuje procesor.
Zastavení animace zajistíme funkcí clearInterval()
, která
bere jako parametr proměnnou obsahující spuštěnou animaci. Protože chceme
animaci ukončit při opuštění stránky, zavoláme tuto funkci v obsluze
události beforeunload
. Celý kód umístíme také do těla metody
addEventListener()
:
window.addEventListener("beforeunload", function() { clearInterval(interval); });
Výsledek:
Nyní bychom měli již zvládat základy práce s animacemi včetně jejich ukončení. Dokázat si to můžeme na cvičení. Jak zajistit, aby za nás webový prohlížeč spouštěl animaci, jen když je potřeba, si řekneme jindy
V následujícím cvičení, Řešené úlohy k 26. lekci JavaScriptu, si procvičíme nabyté zkušenosti z předchozích lekcí.
Měl jsi s čímkoli problém? Stáhni si vzorovou aplikaci níže a porovnej ji se svým projektem, chybu tak snadno najdeš.
Stáhnout
Stažením následujícího souboru souhlasíš s licenčními podmínkami
Staženo 1318x (218.35 kB)
Aplikace je včetně zdrojových kódů v jazyce JavaScript