Moderní tvorba webových aplikací

O webu

Časovače v JavaScriptu

Jaké nabízí JavaScript možnosti pro vytváření animací. A jak docílit toho, aby byly plynulé.

15 minut

Pokud chceme na webu něco pohyblivého/animovaného, nabízejí se 3 řešení:

  1. Použít pohyblivý obrázek GIF / video.
  2. Použít CSS přechody transition nebo vlastnost animation s @keyframes. Tyto CSS animace potom případně spouštět JavaScriptem.
  3. Celou animaci řídit JavaScriptem, který bude měnit CSS vlastnosti (např. umístění, rozměry, průhlednost a podobně) nebo třeba vypisovat nějaký obsah.

Opakované spouštění setInterval

První funkce slouží pro opakované spouštění libovolného JS kódu. Funkce setInterval má dva parametry, kde první je kód, který se má spustit, a druhý časová prodleva v milisekundách.

Existuje několik způsobů, jak kód pro časovač zapsat.

  1. Nejjednodušší je místo prvního parametru napsat řetězec s JS kódem. Následující příklad do elementu s ID „vypsat“ zapíše každých 1000 milisekund (1 vteřina) tečku.

    setInterval(
      "document.getElementById('vypsat').innerHTML += '.'", 
      1000
    );

    Ukázka

    Psaní kódu do řetězce ale není nic dobrého (jedná se o ekvivalent evalu). Proto je lepší se tomuto případu vyhnout. Kromě možných risik při vyhodnocování takového kódu si zbytečně snižujeme čitelnost, protože obsah v řetězci neumí většina editorů správně obarvit.

  2. Funkci setInterval proto raději předáme jako první parametr anonymní/nepojmenovanou funkci:

    setInterval(
      function() {
        // opakovaně spouštěný kód
        document.getElementById('vypsat').innerHTML += '.';
      },
      1000
    );

    Ukázka

  3. Funkci si případně můžeme vytvořit samostatně a setIntervalu předat jen její název:

    function vypsat() {
      // opakovaně spouštěný kód
      document.getElementById('vypsat').innerHTML += '.';
    }
    var casovac = setInterval(vypsat, 1000);

    Pro pozdější zrušení časovače je užitečné si ho přiřadit do proměnné.

    Ukázka

Zrušení intervalu clearInterval

Pokud je interval v nějaké proměnné, jde ho na vyžádání zrušit:

clearInterval(casovac);

Ukázka

Na rušení intervalu je dobré nezapomínat, protože to jinak může způsobovat memory leaky.

Spuštění po čase setTimeout

Na rozdíl od setIntervalu spustí požadovanou akci setTimeout pouze jednou. Jinak jsou si obě funkce dost podobné.

var casovac = setTimeout(
  function() {
    // kód se spustí pouze jednou za 1000 milisekund
  },
  1000
);

Zrušení timeoutu clearTimeout

Pokud je timeout v nějaké proměnné, jde ho na vyžádání zrušit:

clearTimeout(casovac);

Ukázka

Zajímavá je skutečnost, že je možné rušit rovněž setInterval pomocí clearTimeout a naopak.

Opakované spouštění timeoutu

I pomocí setTimeout jde nasimulovat setInterval, tedy zajistit opakované spouštění. V případech, kdy chceme, aby další opakování bylo závislé na tom předchozím, je to i lepší volba.

Celý princip spočívá v rekursivním volání timeoutu. Po provedení vlastní části kódu (vypsání) funkce vypsat zavolá pomocí setTimeout samu sebe.

function vypsat() {
  // samotný výpis
  setTimeout(vypsat, 1000);
}
vypsat();

Jelikož tento kód by běžel nekonečně, možná budeme potřebovat způsob, jak ho zastavit. Existují dvě možnosti.

  1. Použít clearTimeout. Ukázka
  2. Ve funkci vypsat na základě splnění nějaké podmínky další časovač nevytvářet. Ukázka

Časování přes requestAnimationFrame

Novější způsob, kterým disponují prohlížeče od IE 10, je použití metody requestAnimationFrame.

Ta se hodí hlavně pro vytváření animací, protože:

  • Automaticky se synchronisuje s obnovovací frekvencí obrazovky (obvykle 60 FPS).
  • Pozastavuje se, když je stránka v pozadí nebo není viditelná.
  • Poskytuje plynulejší animace než setInterval nebo setTimeout.
  • Optimalisuje výkon baterie na mobilních zařízeních.

Základní použití:

function animace() {
  // kód animace
  requestAnimationFrame(animace);
}
requestAnimationFrame(animace);

Příklad jednoduché animace pohybu elementu:

var element = document.getElementById('animovany');
var pozice = 0;

function animace() {
  pozice += 2;
  element.style.left = pozice + 'px';
  
  if (pozice < 300) {
    requestAnimationFrame(animace);
  }
}

requestAnimationFrame(animace);

Metodě requestAnimationFrame je možné jako druhý parametr předat element, kterého se akce v časovači bude týkat. V takovém případě se nemusí akce provádět, když je například element mimo viditelnou plochu.

Zrušení animace cancelAnimationFrame

Pro zrušení animace slouží funkce cancelAnimationFrame:

var animationId = requestAnimationFrame(animace);
// později...
cancelAnimationFrame(animationId);

Metoda animate

Moderní prohlížeče podporují nativní Web Animations API, které poskytuje metodu element.animate(). Tato metoda umožňuje vytvářet složité animace přímo v JavaScriptu bez nutnosti psát CSS keyframes.

Základní syntaxe:

element.animate(keyframes, options);

Příklad jednoduché animace:

var element = document.getElementById('animovany');

element.animate([
  { transform: 'translateX(0px)' },
  { transform: 'translateX(200px)' }
], {
  duration: 1000,
  easing: 'ease-in-out'
});

Živá ukázka

Pokročilejší animace s více vlastnostmi:

element.animate([
  { 
    opacity: 0,
    transform: 'scale(0.5) translateY(-50px)'
  },
  { 
    opacity: 1,
    transform: 'scale(1) translateY(0px)'
  }
], {
  duration: 800,
  easing: 'cubic-bezier(0.25, 0.46, 0.45, 0.94)',
  fill: 'forwards'
});

Možnosti v options objektu:

  • duration — délka animace v milisekundách,
  • delay — zpoždění před začátkem animace,
  • easing — funkce pro plynulost animace,
  • iterations — počet opakování (Infinity pro nekonečné),
  • direction — směr animace (normal, reverse, alternate, alternate-reverse),
  • fill — chování před a po animaci (none, forwards, backwards, both)

Výhody animate() metody:

  • Nativní podpora v moderních prohlížečích.
  • Lepší výkon než manuální změny CSS vlastností.
  • Možnost programově ovládat animace (pause, play, reverse).
  • Automatická optimalisace prohlížečem.

Ovládání animace:

var animace = element.animate([...], {...});

// pozastavení
animace.pause();

// pokračování
animace.play();

// obrácení
animace.reverse();

// nastavení rychlosti
animace.playbackRate = 2; // dvojnásobná rychlost


Problém s neaktivními taby

Prohlížeče zpomalují nebo pozastavují JavaScript v neaktivních tabech, což ovlivňuje časování a animace.

Různé prohlížeče se chovají jinak, ale obecně jde vycházet z:

  • omezení setTimeout/setInterval na cca 1 sekundu,
  • requestAnimationFrame se pozastaví,
  • na mobilních zařízeních se pozastaví většina JavaScriptu

Chování se může lišit i podle aktuálního stavu baterie (úsporný režim apod.).

Reagovat na neaktivní taby lze pomocí visibilitychange.

// Detekce viditelnosti stránky
document.addEventListener('visibilitychange', () => {
  if (document.hidden) {
    // Pozastavit animace
    animation.pause();
  } else {
    // Obnovit animace
    animation.play();
  }
});

Další možnosti časování

Časování přes Promise

Na pozadí používá setTimeout pro vytvoření zpoždění/sleep.

// Jednoduché zpoždění
function delay(ms) {
  return new Promise(resolve => setTimeout(resolve, ms));
}

// Použití
async function animovanaSekvence() {
  console.log('Začátek');
  await delay(1000);
  console.log('Po 1 sekundě');
  await delay(2000);
  console.log('Po dalších 2 sekundách');
}

animovanaSekvence();

Časování ve Web Workers

Pro náročné operace, které by mohly blokovat hlavní vlákno, lze použít Web Workers:

// main.js
const worker = new Worker('timer-worker.js');

worker.postMessage({ type: 'start', interval: 1000 });

worker.onmessage = function(e) {
  if (e.data.type === 'tick') {
    console.log('Tick z workeru:', e.data.timestamp);
  }
};

// timer-worker.js
self.onmessage = function(e) {
  if (e.data.type === 'start') {
    setInterval(() => {
      self.postMessage({
        type: 'tick',
        timestamp: Date.now()
      });
    }, e.data.interval);
  }
};

Performance API pro přesné měření

Pro velmi přesné měření času lze použít Performance API:

// Měření času s vysokou přesností
const start = performance.now();

setTimeout(() => {
  const end = performance.now();
  console.log(`Uplynulo: ${end - start} milisekund`);
}, 1000);

Throttling a debouncing

Oba přístupy omezují frekvenci spouštění funkcí, čímž snižují náročnost, ale fungují jinak:

Throttling

Throttling (česky omezování frekvence nebo škrcení) omezuje počet spuštění funkce na maximální hodnotu. Funkce se spustí maximálně jednou za daný časový interval.

// Throttling - omezí frekvenci spouštění
function throttle(func, limit) {
  let inThrottle;
  return function() {
    const args = arguments;
    const context = this;
    if (!inThrottle) {
      func.apply(context, args);
      inThrottle = true;
      setTimeout(() => inThrottle = false, limit);
    }
  }
}

// Použití pro scroll události
window.addEventListener('scroll', throttle(() => {
  console.log('Došlo ke scrollování');
}, 100));

Kdy použít throttling:

  • Scroll události – reagování na rolování na stránce, které není potřeba tak často.
  • Mouse move události – pro sledování pohybu myši.
  • Resize události – změna velikosti okna.
  • API volání – pro omezení rate limitů.

Debouncing

Debouncing (česky odložení spuštění) počká na konec série událostí a spustí funkci až po určité době klidu. Funkce se spustí pouze jednou po poslední události.

// Debouncing - počká na konec série událostí
function debounce(func, delay) {
  let timeoutId;
  return function() {
    const args = arguments;
    const context = this;
    clearTimeout(timeoutId);
    timeoutId = setTimeout(() => func.apply(context, args), delay);
  }
}

// Použití pro resize události
window.addEventListener('resize', debounce(() => {
  console.log('Došlo ke změně velikosti');
}, 250));

Debouncing se hodí použít, když není potřeba reagovat ihned na každou změnu. Ale stačí to až po nějaké době nečinnosti:

  • vyhledávání – pro vyhledávání při psaní nemusí být efektivní hledat po zadání každého znaku,
  • validace formulářů – nemusí být potřeba validovat okamžitě,
  • automatické ukládání

Hodí se zvlášť pro výpočetně náročné operace nebo API volání. Pro rychlé věci může být uživatelsky přívětivější reagovat hned.

Kdy použít kterou metodu

Výběr správné metody závisí na konkrétním použití:

  • setInterval/setTimeout – pro jednoduché opakované akce, které nejsou visuální animace,
  • requestAnimationFrame – pro plynulé visuální animace a hry,
  • element.animate() – pro deklarativní animace s komplexními přechody,
  • CSS animace – pro jednoduché přechody a animace, které nevyžadují JavaScript logiku,
  • Web Workers – pro náročné výpočty bez blokování UI

Odkazy jinam

Související články

Detekce otevření DevTools

Jak zjistit, že se na stránce otevřely vývojářské nástroje.

13 minut

JavaScript null a undefined

Rozdíly mezi null a undefined v JavaScriptu, kdy je používat a jak se vyhnout běžným chybám.

12 minut

Sleep v JavaScriptu

Jak implementovat sleep/delay funkcionalitu v JavaScriptu pomocí Promise a async/await

6 minut

JavaScript Battery API

Jak v JS zjistit stav baterie, co dnes funguje a kdy API nepoužívat.

3 minuty

Web jecas.cz píše Bohumil Jahoda, kontakt
Seznam všech článků
2013–2025