CSS-animace

Animace CSS umožňují provádět jednoduché animace zcela bez JavaScriptu.

JavaScript lze použít k ovládání animací CSS a jejich ještě lepšímu, s malým množstvím kódu.

Přechody CSS

Myšlenka přechodů CSS je jednoduchá. Popisujeme vlastnost a jak by měly být její změny animovány. Když se vlastnost změní, prohlížeč vykreslí animaci.

To znamená, že vše, co potřebujeme, je změnit vlastnost a plynulý přechod provede prohlížeč.

Například CSS níže animuje změny background-color po dobu 3 sekund:

.animated {
  transition-property: background-color;
  transition-duration: 3s;
}

Nyní, pokud má prvek .animated třída, jakákoliv změna background-color se animuje po dobu 3 sekund.

Klepnutím na tlačítko níže animujete pozadí:

<button id="color">Click me</button>

<style>
  #color {
    transition-property: background-color;
    transition-duration: 3s;
  }
</style>

<script>
  color.onclick = function() {
    this.style.backgroundColor = 'red';
  };
</script>

K popisu přechodů CSS existují 4 vlastnosti:

  • transition-property
  • transition-duration
  • transition-timing-function
  • transition-delay

Za chvíli se jim budeme věnovat, prozatím si všimněme běžného transition vlastnost umožňuje jejich společné deklarování v pořadí:property duration timing-function delay , stejně jako animace více vlastností najednou.

Toto tlačítko například animuje obě color a font-size :

<button id="growing">Click me</button>

<style>
#growing {
  transition: font-size 3s, color 2s;
}
</style>

<script>
growing.onclick = function() {
  this.style.fontSize = '36px';
  this.style.color = 'red';
};
</script>

Nyní si proberme vlastnosti animace jednu po druhé.

vlastnost přechodu

V transition-property , napíšeme seznam vlastností k animaci, například:left , margin-left , height , color . Nebo bychom mohli napsat all , což znamená „animovat všechny vlastnosti“.

Všimněte si, že existují vlastnosti, které nelze animovat. Většina obecně používaných vlastností je však animovatelná.

transition-duration

V transition-duration můžeme určit, jak dlouho má animace trvat. Čas by měl být ve formátu času CSS:v sekundách s nebo milisekundy ms .

zpoždění přechodu

V transition-delay můžeme určit zpoždění před animace. Pokud například transition-delay je 1s a transition-duration je 2s , pak se animace spustí 1 sekundu po změně vlastnosti a celková doba trvání bude 2 sekundy.

Možné jsou i záporné hodnoty. Poté se animace zobrazí okamžitě, ale počáteční bod animace bude po zadané hodnotě (čase). Pokud například transition-delay je -1s a transition-duration je 2s , pak animace začne od poloviny a celková délka bude 1 sekunda.

Zde animace posouvá čísla z 0 na 9 pomocí CSS translate vlastnost:

Resultscript.jsstyle.cssindex.html
stripe.onclick = function() {
  stripe.classList.add('animate');
};
#digit {
  width: .5em;
  overflow: hidden;
  font: 32px monospace;
  cursor: pointer;
}

#stripe {
  display: inline-block
}

#stripe.animate {
  transform: translate(-90%);
  transition-property: transform;
  transition-duration: 9s;
  transition-timing-function: linear;
}
<!doctype html>
<html>

<head>
  <meta charset="UTF-8">
  <link rel="stylesheet" href="style.css">
</head>

<body>

  Click below to animate:

  <div id="digit"><div id="stripe">0123456789</div></div>

  <script src="script.js"></script>
</body>

</html>

transform vlastnost je animována takto:

#stripe.animate {
  transform: translate(-90%);
  transition-property: transform;
  transition-duration: 9s;
}

Ve výše uvedeném příkladu JavaScript přidá třídu .animate k prvku – a animace se spustí:

stripe.classList.add('animate');

Mohli bychom to také začít někde z poloviny přechodu, z přesného čísla, kupř. odpovídající aktuální sekundě pomocí záporné transition-delay .

Zde, pokud kliknete na číslici – spustí se animace od aktuální sekundy:

Resultscript.jsstyle.cssindex.html
stripe.onclick = function() {
  let sec = new Date().getSeconds() % 10;
  stripe.style.transitionDelay = '-' + sec + 's';
  stripe.classList.add('animate');
};
#digit {
  width: .5em;
  overflow: hidden;
  font: 32px monospace;
  cursor: pointer;
}

#stripe {
  display: inline-block
}

#stripe.animate {
  transform: translate(-90%);
  transition-property: transform;
  transition-duration: 9s;
  transition-timing-function: linear;
}
<!doctype html>
<html>

<head>
  <meta charset="UTF-8">
  <link rel="stylesheet" href="style.css">
</head>

<body>

  Click below to animate:
  <div id="digit"><div id="stripe">0123456789</div></div>

  <script src="script.js"></script>
</body>
</html>

JavaScript to dělá s řádkem navíc:

stripe.onclick = function() {
  let sec = new Date().getSeconds() % 10;
  // for instance, -3s here starts the animation from the 3rd second
  stripe.style.transitionDelay = '-' + sec + 's';
  stripe.classList.add('animate');
};

funkce-časování-přechodu

Funkce časování popisuje, jak je proces animace rozložen na své časové ose. Začne to pomalu a pak to půjde rychle, nebo naopak.

Zpočátku se jeví jako nejsložitější vlastnost. Ale bude to velmi jednoduché, pokud tomu věnujeme trochu času.

Tato vlastnost přijímá dva druhy hodnot:Bézierovu křivku nebo kroky. Začněme křivkou, protože se používá častěji.

Bezierova křivka

Funkci časování lze nastavit jako Bézierovu křivku se 4 kontrolními body, které splňují podmínky:

  1. První kontrolní bod:(0,0) .
  2. Poslední kontrolní bod:(1,1) .
  3. Pro mezilehlé body hodnoty x musí být v intervalu 0..1 , y může být cokoliv.

Syntaxe Bézierovy křivky v CSS:cubic-bezier(x2, y2, x3, y3) . Zde musíme zadat pouze 2. a 3. kontrolní bod, protože první je pevně nastaven na (0,0) a čtvrtý je (1,1) .

Funkce časování popisuje, jak rychle proces animace probíhá.

  • x osa je čas:0 – začátek, 1 – konec transition-duration .
  • y osa určuje dokončení procesu:0 – počáteční hodnota vlastnosti, 1 – konečná hodnota.

Nejjednodušší varianta je, když animace probíhá rovnoměrně, se stejnou lineární rychlostí. To lze specifikovat křivkou cubic-bezier(0, 0, 1, 1) .

Takto vypadá křivka:

…Jak vidíme, je to jen přímka. Jako čas (x ) projde, dokončení (y ) animace plynule přechází z 0 na 1 .

Vlak v příkladu níže jede zleva doprava stálou rychlostí (klikněte na něj):

Resultsstyle.cssindex.html
.train {
  position: relative;
  cursor: pointer;
  width: 177px;
  height: 160px;
  left: 0;
  transition: left 5s cubic-bezier(0, 0, 1, 1);
}
<!doctype html>
<html>

<head>
  <meta charset="UTF-8">
  <link rel="stylesheet" href="style.css">
</head>

<body>

  <img class="train" src="https://js.cx/clipart/train.gif" onclick="this.style.left='450px'">

</body>

</html>

CSS transition je založen na této křivce:

.train {
  left: 0;
  transition: left 5s cubic-bezier(0, 0, 1, 1);
  /* click on a train sets left to 450px, thus triggering the animation */
}

…A jak můžeme ukázat zpomalení vlaku?

Můžeme použít jinou Bezierovu křivku:cubic-bezier(0.0, 0.5, 0.5 ,1.0) .

Graf:

Jak vidíme, proces začíná rychle:křivka stoupá nahoru a pak pomaleji a pomaleji.

Zde je funkce měření času v akci (klikněte na vlak):

Resultsstyle.cssindex.html
.train {
  position: relative;
  cursor: pointer;
  width: 177px;
  height: 160px;
  left: 0px;
  transition: left 5s cubic-bezier(0.0, 0.5, 0.5, 1.0);
}
<!doctype html>
<html>

<head>
  <meta charset="UTF-8">
  <link rel="stylesheet" href="style.css">
</head>

<body>

  <img class="train" src="https://js.cx/clipart/train.gif" onclick="this.style.left='450px'">

</body>

</html>

CSS:

.train {
  left: 0;
  transition: left 5s cubic-bezier(0, .5, .5, 1);
  /* click on a train sets left to 450px, thus triggering the animation */
}

Existuje několik vestavěných křivek:linear , ease , ease-in , ease-out a ease-in-out .

linear je zkratka pro cubic-bezier(0, 0, 1, 1) – přímka, kterou jsme popsali výše.

Ostatní názvy jsou zkratky pro následující cubic-bezier :

ease * ease-in ease-out ease-in-out
(0.25, 0.1, 0.25, 1.0) (0.42, 0, 1.0, 1.0) (0, 0, 0.58, 1.0) (0.42, 0, 0.58, 1.0)

* – ve výchozím nastavení, pokud není k dispozici funkce časování, ease se používá.

Takže bychom mohli použít ease-out pro náš zpomalující vlak:

.train {
  left: 0;
  transition: left 5s ease-out;
  /* same as transition: left 5s cubic-bezier(0, .5, .5, 1); */
}

Ale vypadá to trochu jinak.

Bézierová křivka může způsobit, že animace překročí její rozsah.

Řídicí body na křivce mohou mít libovolný y souřadnice:i záporné nebo obrovské. Pak by se Bézierova křivka také rozšířila velmi nízko nebo vysoko, čímž by animace přesáhla svůj normální rozsah.

V příkladu níže je kód animace:

.train {
  left: 100px;
  transition: left 5s cubic-bezier(.5, -1, .5, 2);
  /* click on a train sets left to 450px */
}

Vlastnost left by se měl animovat z 100px na 400px .

Ale pokud kliknete na vlak, uvidíte toto:

  • Nejprve vlak jede zpět :left bude menší než 100px .
  • Pak jde dopředu, o něco dál než 400px .
  • A pak znovu – na 400px .
Resultsstyle.cssindex.html
.train {
  position: relative;
  cursor: pointer;
  width: 177px;
  height: 160px;
  left: 100px;
  transition: left 5s cubic-bezier(.5, -1, .5, 2);
}
<!doctype html>
<html>

<head>
  <meta charset="UTF-8">
  <link rel="stylesheet" href="style.css">
</head>

<body>

  <img class="train" src="https://js.cx/clipart/train.gif" onclick="this.style.left='400px'">

</body>

</html>

Proč se to děje, je docela zřejmé, když se podíváme na graf dané Bezierovy křivky:

Přesunuli jsme y souřadnice 2. bodu pod nulou a pro 3. bod jsme to udělali přes 1 , takže křivka jde mimo „běžný“ kvadrant. y je mimo „standardní“ rozsah 0..1 .

Jak víme, y měří „dokončení animačního procesu“. Hodnota y = 0 odpovídá počáteční hodnotě vlastnosti a y = 1 – konečná hodnota. Tedy hodnoty y<0 přesunout vlastnost za počáteční left a y>1 – za poslední left .

To je určitě „měkká“ varianta. Pokud vložíme y hodnoty jako -99 a 99 pak by vlak vyskočil z dosahu mnohem více.

Jak ale vytvoříme Bezierovu křivku pro konkrétní úlohu? Existuje mnoho nástrojů.

  • Můžeme to udělat například na webu https://cubic-bezier.com.
  • Nástroje pro vývojáře prohlížeče mají také speciální podporu pro Bezierovy křivky v CSS:
    1. Otevřete nástroje pro vývojáře pomocí F12 (Mac:Cmd+Opt+I ).
    2. Vyberte Elements a poté věnujte pozornost Styles dílčí panel na pravé straně.
    3. Vlastnosti CSS se slovem cubic-bezier bude mít před tímto slovem ikonu.
    4. Kliknutím na tuto ikonu křivku upravíte.

Kroky

Funkce časování steps(number of steps[, start/end]) umožňuje rozdělit přechod do více kroků.

Podívejme se na to na příkladu s číslicemi.

Zde je seznam číslic bez jakýchkoli animací, jen jako zdroj:

Resultsstyle.cssindex.html
#digit {
  border: 1px solid red;
  width: 1.2em;
}

#stripe {
  display: inline-block;
  font: 32px monospace;
}
<!DOCTYPE html>
<html>

<head>
  <meta charset="utf-8">
  <link rel="stylesheet" href="style.css">
</head>

<body>

  <div id="digit"><div id="stripe">0123456789</div></div>

</body>
</html>

V kódu HTML je pruh číslic uzavřen v pevné délce <div id="digits"> :

<div id="digit">
  <div id="stripe">0123456789</div>
</div>

#digit div má pevnou šířku a ohraničení, takže vypadá jako červené okno.

Vytvoříme časovač:číslice se budou objevovat jedna po druhé, diskrétním způsobem.

Abychom toho dosáhli, skryjeme #stripe mimo #digit pomocí overflow: hidden a poté posuňte #stripe doleva krok za krokem.

K dispozici bude 9 kroků, krok pro každou číslici:

#stripe.animate  {
  transform: translate(-90%);
  transition: transform 9s steps(9, start);
}

První argument steps(9, start) je počet kroků. Transformace bude rozdělena na 9 částí (každá po 10 %). Časový interval je také automaticky rozdělen na 9 částí, takže transition: 9s nám dává 9 sekund na celou animaci – 1 sekundu na číslici.

Druhý argument je jedno ze dvou slov:start nebo end .

start znamená, že na začátku animace musíme okamžitě udělat první krok.

V akci:

Resultsstyle.cssindex.html
#digit {
  width: .5em;
  overflow: hidden;
  font: 32px monospace;
  cursor: pointer;
}

#stripe {
  display: inline-block
}

#stripe.animate {
  transform: translate(-90%);
  transition-property: transform;
  transition-duration: 9s;
  transition-timing-function: steps(9, start);
}
<!DOCTYPE html>
<html>

<head>
  <meta charset="utf-8">
  <link rel="stylesheet" href="style.css">
</head>

<body>

  Click below to animate:

  <div id="digit"><div id="stripe">0123456789</div></div>

  <script>
    digit.onclick = function() {
      stripe.classList.add('animate');
    }
  </script>


</body>

</html>

Kliknutím na číslici se změní na 1 (první krok) okamžitě a poté se změní na začátku další sekundy.

Proces probíhá takto:

  • 0s-10% (první změna na začátku 1. sekundy, okamžitě)
  • 1s-20%
  • 8s-90%
  • (poslední sekunda ukazuje konečnou hodnotu).

Zde byla první změna okamžitá, protože start v steps .

Alternativní hodnota end by znamenalo, že změna by neměla být aplikována na začátku, ale na konci každé sekundy.

Takže proces pro steps(9, end) bude vypadat takto:

  • 0s0 (během první vteřiny se nic nemění)
  • 1s-10% (první změna na konci 1. sekundy)
  • 2s-20%
  • 9s-90%

Zde je steps(9, end) v akci (všimněte si pauzy před první změnou číslice):

Resultsstyle.cssindex.html
#digit {
  width: .5em;
  overflow: hidden;
  font: 32px monospace;
  cursor: pointer;
}

#stripe {
  display: inline-block
}

#stripe.animate {
  transform: translate(-90%);
  transition-property: transform;
  transition-duration: 9s;
  transition-timing-function: steps(9, end);
}
<!DOCTYPE html>
<html>

<head>
  <meta charset="utf-8">
  <link rel="stylesheet" href="style.css">
</head>

<body>

  Click below to animate:

  <div id="digit"><div id="stripe">0123456789</div></div>

  <script>
    digit.onclick = function() {
      stripe.classList.add('animate');
    }
  </script>


</body>

</html>

Existuje také několik předdefinovaných zkratek pro steps(...) :

  • step-start – je stejné jako steps(1, start) . To znamená, že animace se spustí okamžitě a provede 1 krok. Takže to začne a skončí okamžitě, jako by žádná animace nebyla.
  • step-end – stejně jako steps(1, end) :vytvořte animaci v jediném kroku na konci transition-duration .

Tyto hodnoty se používají zřídka, protože nepředstavují skutečnou animaci, ale spíše jednokrokovou změnu. Pro úplnost je zde uvádíme.

Událost:„transitionend“

Po dokončení animace CSS se zobrazí transitionend spouštěče událostí.

Je široce používán k provedení akce po dokončení animace. Také se můžeme připojit k animacím.

Například loď v níže uvedeném příkladu po kliknutí začne plout tam a zpět, pokaždé dále a dále doprava:

Animace je spuštěna funkcí go který se znovu spustí pokaždé, když přechod skončí, a otočí směr:

boat.onclick = function() {
  //...
  let times = 1;

  function go() {
    if (times % 2) {
      // sail to the right
      boat.classList.remove('back');
      boat.style.marginLeft = 100 * times + 200 + 'px';
    } else {
      // sail to the left
      boat.classList.add('back');
      boat.style.marginLeft = 100 * times - 200 + 'px';
    }

  }

  go();

  boat.addEventListener('transitionend', function() {
    times++;
    go();
  });
};

Objekt události pro transitionend má několik specifických vlastností:

event.propertyName
Vlastnost, která dokončila animaci. Může být dobré, když animujeme více vlastností současně.
event.elapsedTime
Čas (v sekundách), který animace zabrala, bez transition-delay .

Klíčové snímky

Můžeme spojit několik jednoduchých animací dohromady pomocí @keyframes pravidlo CSS.

Specifikuje „název“ animace a pravidla – co, kdy a kde animovat. Poté pomocí animation vlastnost, můžeme k prvku připojit animaci a specifikovat pro ni další parametry.

Zde je příklad s vysvětlením:

<div class="progress"></div>

<style>
  @keyframes go-left-right {        /* give it a name: "go-left-right" */
    from { left: 0px; }             /* animate from left: 0px */
    to { left: calc(100% - 50px); } /* animate to left: 100%-50px */
  }

  .progress {
    animation: go-left-right 3s infinite alternate;
    /* apply the animation "go-left-right" to the element
       duration 3 seconds
       number of times: infinite
       alternate direction every time
    */

    position: relative;
    border: 2px solid green;
    width: 50px;
    height: 20px;
    background: lime;
  }
</style>

Existuje mnoho článků o @keyframes a podrobnou specifikaci.

Pravděpodobně nebudete potřebovat @keyframes často, pokud není na vašich stránkách vše v neustálém pohybu.

Výkon

Většinu vlastností CSS lze animovat, protože většina z nich jsou číselné hodnoty. Například width , color , font-size jsou všechna čísla. Když je animujete, prohlížeč tato čísla postupně mění snímek po snímku, čímž vytvoří plynulý efekt.

Ne všechny animace však budou vypadat tak hladce, jak byste chtěli, protože změny různých vlastností CSS jsou různé.

V techničtějších detailech, když dojde ke změně stylu, prohlížeč projde 3 kroky k vykreslení nového vzhledu:

  1. Rozvržení :přepočítat geometrii a polohu každého prvku a poté
  2. Vybarvit :přepočítejte, jak by vše mělo vypadat na svém místě, včetně pozadí, barev,
  3. Kompozitní :vykreslí konečné výsledky do obrazových bodů na obrazovce, použije transformace CSS, pokud existují.

Během animace CSS se tento proces opakuje každý snímek. Nicméně vlastnosti CSS, které nikdy neovlivňují geometrii nebo pozici, jako je color , může přeskočit krok Rozvržení. Pokud color změní, prohlížeč nevypočítá žádnou novou geometrii, přejde do Malování → Kompozitní. A existuje jen málo vlastností, které jdou přímo do Composite. Delší seznam vlastností CSS a fází, které spouštějí, najdete na https://csstriggers.com.

Výpočty mohou nějakou dobu trvat, zvláště na stránkách s mnoha prvky a složitým rozvržením. A zpoždění jsou skutečně vidět na většině zařízení, což vede k „chvějícím se“, méně plynulým animacím.

Animace vlastností, které přeskakují krok Rozvržení, jsou rychlejší. Ještě lepší je, když je Malování také přeskočeno.

transform vlastnost je skvělá volba, protože:

  • Transformace CSS ovlivňují rámeček cílového prvku jako celek (otočit, převrátit, roztáhnout, posouvat).
  • Transformace CSS nikdy neovlivní sousední prvky.

…Prohlížeče tedy použijí transform „navrch“ existujících výpočtů rozvržení a malby ve fázi Složený.

Jinými slovy, prohlížeč vypočítá rozvržení (velikosti, pozice), vybarví jej barvami, pozadím atd. ve fázi Malování a poté použije transform do polí prvků, které to potřebují.

Změny (animace) transform vlastnost nikdy nespustí kroky rozvržení a malování. Prohlížeč navíc využívá grafický akcelerátor (speciální čip na CPU nebo grafické kartě) pro transformace CSS, takže jsou velmi efektivní.

Naštěstí transform majetek je velmi silný. Pomocí transform na prvku jej můžete otáčet a převracet, natahovat a zmenšovat, pohybovat s ním a mnoho dalšího. Takže místo left/margin-left vlastnosti můžeme použít transform: translateX(…) , použijte transform: scale pro zvětšení velikosti prvku atd.

opacity vlastnost také nikdy nespustí Layout (také přeskočí Malování v Mozilla Gecko). Můžeme jej použít pro efekty show/hide nebo fade-in/fade-out.

Párování transform s opacity dokáže obvykle vyřešit většinu našich potřeb a poskytuje plynulé, dobře vypadající animace.

Například zde kliknutím na #boat element přidá třídu s transform: translateX(300) a opacity: 0 , čímž se přesune na 300px doprava a zmizí:

<img src="https://js.cx/clipart/boat.png" id="boat">

<style>
#boat {
  cursor: pointer;
  transition: transform 2s ease-in-out, opacity 2s ease-in-out;
}

.move {
  transform: translateX(300px);
  opacity: 0;
}
</style>
<script>
  boat.onclick = () => boat.classList.add('move');
</script>

Zde je složitější příklad s @keyframes :

<h2 onclick="this.classList.toggle('animated')">click me to start / stop</h2>
<style>
  .animated {
    animation: hello-goodbye 1.8s infinite;
    width: fit-content;
  }
  @keyframes hello-goodbye {
    0% {
      transform: translateY(-60px) rotateX(0.7turn);
      opacity: 0;
    }
    50% {
      transform: none;
      opacity: 1;
    }
    100% {
      transform: translateX(230px) rotateZ(90deg) scale(0.5);
      opacity: 0;
    }
  }
</style>

Shrnutí

Animace CSS umožňují plynule (nebo krok za krokem) animované změny jedné nebo více vlastností CSS.

Jsou dobré pro většinu animačních úkolů. Pro animace jsme také schopni použít JavaScript, tomu je věnována další kapitola.

Omezení CSS animací ve srovnání s JavaScriptovými animacemi:

Přednosti
  • Jednoduché věci dělané jednoduše.
  • Rychlý a lehký pro CPU.
Vnady
  • JavaScriptové animace jsou flexibilní. Mohou implementovat libovolnou animační logiku, jako je „exploze“ prvku.
  • Nejen změny vlastností. V rámci animace můžeme vytvořit nové prvky v JavaScriptu.

V prvních příkladech v této kapitole animujeme font-size , left , width , height , atd. V reálných projektech bychom měli používat transform: scale() a transform: translate() pro lepší výkon.

Většinu animací lze implementovat pomocí CSS, jak je popsáno v této kapitole. A transitionend událost umožňuje spuštění JavaScriptu po animaci, takže se dobře integruje s kódem.

Ale v další kapitole uděláme několik JavaScriptových animací, abychom pokryli složitější případy.


No