Jednou z věcí, které nás při tvorbě nového webu mátly, byly morfované vlny, které můžete vidět na různých místech stránek. Měli jsme mnoho nápadů, jak je správně implementovat bez velkého úsilí. Většina řešení však byla pomalá a museli jsme od základu vytvořit něco, co by bylo rychlejší než již existující knihovny.
Návrhy řešení
Začali jsme s obyčejným objektem SVG, který jsme animovali pomocí různých knihoven. Protože jsme chtěli mít na jedné stránce 3 objekty, výsledek nebyl tak uspokojivý. Všechny animace byly prostě pomalé - všechny cesty jednoho objektu SVG se musely aktualizovat v opravdu krátkých časových úsecích, což způsobilo, že celá stránka byla pomalá jako šnek. Řešení s čistým SVG vloženým do dokumentu jsme museli zavrhnout. Tím pádem zůstalo nás s možností výběru ze dvou dalších řešení.
Na stránkách video prvek byl druhou možností. Začali jsme se dvěma problémy:
- průhledné pozadí, které nelze použít u nejoblíbenějších formátů videa, jako je .mp4 nebo .webm,
- odezva, což byl skutečný problém, protože videa jako taková nejsou škálovatelná.
Rozhodli jsme se ponechat toto řešení v záloze - "pokud nenajdeme nic jiného, vybereme toto".
Poslední možností bylo použít plátno s WebGL vykreslování. Byla to taková neobvyklá možnost, protože jsme museli sami navrhnout všechny mechanismy vykreslování. To proto, že morfické vlny, které jsme měli, byly vlastní - to nás nutilo navrhnout vlastní řešení A to byla možnost, kterou jsme chtěli následovat a na kterou jsme se opravdu zaměřili.
Architektura řešení
Začněme od začátku. Z jakého materiálu jsme museli tyto vlny postavit? Představa byla taková, že všechny vlny byly souborem SVG o velikosti 1 × 1 a specifickými cestami umístěnými kolem této oblasti. Animace tohoto SVG byla sestavena pomocí některých forem stavů k tomuto souboru. Všechny animace tedy byly reprezentovány jako soubor souborů, které obsahovaly fáze pohybu tvaru.
Podívejte se hlouběji na to, co jsou to stavy - všechny cesty jsou jen jakýmsi polem s konkrétními hodnotami umístěnými v konkrétním pořadí. Změna těchto hodnot na konkrétních pozicích v tomto poli mění tvar v jeho konkrétních částech. Můžeme si to zjednodušit na následujícím příkladu:
stav 1: [1, 50, 25, 40, 100]
stav 2: [0, 75, -20, 5, 120]
stav 3: [5, 0, -100, 80, 90]
Můžeme tedy předpokládat, že tvar, který chceme vykreslit, se skládá z pole o 5 prvcích, které se mění s lineárním zklidněním v určitých časových úsecích. Jakmile animace dokončí poslední fázi, spustí se zpět od první, abychom získali dojem nekonečné animace.
Ale... počkat - co přesně je výše uvedené pole? Jak jsem se již zmínil, je to cesta, která je zodpovědná za zobrazení určitého tvaru. Všechna tato kouzla jsou obsažena v d vlastnosti cesty SVG. Tato vlastnost obsahuje sadu "příkazů" pro vykreslení tvaru a každý příkaz má určitý druh argumentů. Výše uvedené pole se skládá ze všech hodnot (argumentů) připojených k těmto příkazům.
Jediným rozdílem mezi těmito "stavovými soubory" byly hodnoty konkrétních příkazů, protože pořadí příkazů bylo stejné. Celé kouzlo tedy spočívalo v získání všech hodnot a jejich animování.
Průvodce s názvem Fyzika
V předchozím odstavci jsem zmínil, že jediným kouzlem animace objektu je vytvoření přechodů mezi všemi fázemi tvaru. Otázka zní - jak to udělat s plátnem?
Funkce, kterou každý, kdo pracoval s plátno by měl vědět, že requestAnimationFrame. Pokud to vidíte poprvé, upřímně věřím, že byste měli začít tím, že si to přečtete. Takže to, co nás u této funkce zajímá, je argument - DOMHighResTimeStamp. Vypadá to opravdu děsivě, ale v praxi to není tak těžké pochopit. Můžeme říci, že se jedná o časovou značku uplynulého času od prvního vykreslení.
Dobře, ale co s tím můžeme dělat? Protože requestAnimationFrame by měla být funkce volána rekurzivně, můžeme přistupovat k časovému odstupu mezi jejími voláními. A jsme u té vědy! ⚛️ (dobře, možná to není raketová věda... zatím )
Fyzika nás učí, že delta vzdálenosti se rovná deltě času vynásobené rychlostí. V našem případě je rychlost konstantní, protože chceme dosáhnout konečného bodu za určitý čas. Podívejme se tedy, jak to můžeme znázornit pomocí výše uvedených stavů:
Řekněme, že chceme přecházet mezi těmito stavy za tisíc milisekund, takže hodnoty rychlosti budou následující:
delta: [ -1, 25, -45, -35, 20]
rychlost: [-1/1000, 25/1000, -45/1000, -35/1000, 20/1000]
Výše uvedená rychlost nám říká: za každou milisekundu zvyšme hodnotu o -1/1000. A tady je bod, kdy se můžeme vrátit k našemu requestAnimationFrame a časovou deltu. Hodnota konkrétní polohy, o kterou chceme zvýšit, je čas delta vynásobený rychlostí její polohy. Ještě jedné věci dosáhneme bez problému, a to omezení hodnoty tak, aby nepřekročila stav, do kterého směřuje.
Když přechod skončí, zavoláme další a tak dále. A nezdá se, že by to bylo tak těžké implementovat, ale jedno z hlavních pravidel v systému vývoj softwaru je netrávit čas věcmi, které jsou již implementovány. Takže - vybrali jsme malá knihovna který nám umožňuje tyto přechody vytvářet bez námahy.
Tak jsme vytvořili jednu animovanou vlnu, která vypadá jako živý tvor.
Několik slov o klonování tvarů
Jak vidíte, vlny značky The Codest nejsou jedinou animovanou postavičkou. Skládají se z mnoha figurek stejného tvaru, ale různé velikosti a polohy. V tomto kroku se rychle podíváme na to, jak takovým způsobem duplikovat.
Kontext plátna nám tedy umožňuje oblast kreslení měřítka (pod kapotou - můžeme říci, že násobí všechny rozměry předávané do metod drawable hodnotou "k", kde "k" je měřítko, ve výchozím nastavení "1"), provést překlad plátna, je to jako změna kotevního bodu oblasti kreslení. A mezi těmito stavy můžeme pomocí těchto metod také přecházet: uložit a obnovit.
Tyto metody nám umožňují uložit stav "nulových modifikací" a poté vykreslit určitý počet vln ve smyčce se správně škálovaným a přeloženým plátnem. Hned poté se můžeme vrátit do uloženého stavu. To je vše o klonování obrázků. Mnohem jednodušší než klonování ovcí, že?
Třešnička na dortu
Zmínil jsem, že jsme jedno z možných řešení odmítli kvůli výkonu. Varianta s plátnem je docela rychlá, ale nikdo neřekl, že by se nedala optimalizovat ještě více. Začněme tím, že nás vlastně nezajímá přechod tvarů, když jsou mimo viewport prohlížeče.
Existuje další rozhraní API prohlížeče, které si programátoři oblíbili - IntersectionObserver. Umožňuje nám sledovat konkrétní prvky stránky a zpracovávat události, které jsou vyvolány, když se tyto prvky objeví nebo zmizí z viewportu. Právě teď - máme celkem jednoduchou situaci - vytvoříme stav viditelnosti, změníme jej díky obsluze událostí IntersectionObserver a jednoduše zapneme/vypneme vykreslovací systém pro konkrétní tvary. A ... bum, výkon se o hodně zlepšil! Vykreslujeme pouze věci, které jsou viditelné na viewportu.
Souhrn
Vybrat způsob implementace je často těžká volba, zejména když se zdá, že dostupné možnosti mají podobné výhody i nevýhody. Klíčem ke správné volbě je analyzovat každou z nich a vyloučit ty, které považujeme za méně výhodné. Ne vše je jasné - některé řešení vyžaduje více času než ostatní, ale může být snadněji optimalizovatelné nebo lépe přizpůsobitelné.
Přestože se nové knihovny JS objevují téměř každou minutu, některé věci neřeší. A právě proto by měl každý front-end inženýr (a nejen on) znát API prohlížeče, sledovat technické novinky a občas se prostě zamyslet "jak by vypadala moje implementace této knihovny, kdybych musel udělat tohle?". S většími znalostmi o prohlížečích můžeme vytvářet opravdu efektní věci, správně se rozhodovat o nástrojích, které používáme, a být si jistější v kramflecích. kód.
Přečtěte si více:
– Ruby 3.0. Ruby a méně známé metody kontroly soukromí
– Sklapni a vezmi si své peníze #1: Skryté náklady a skutečná agilita v procesu vývoje produktu
– Výzvy CTO - rozšiřování a růst softwarových produktů