Responzivní a nekonečně škálovatelné JS animace

Na konci roku 2012 nebylo snadné najít open source projekty pomocí requestAnimationFrame() - toto je háček, který umožňuje synchronizaci kódu Javascript s nativní smyčkou malování webového prohlížeče. Animace využívající tuto metodu mohou běžet rychlostí 60 snímků za sekundu a poskytovat fantastické interakce s obsahem podobným hře, pokud budete dbát na dodržování pravidel.*

V této době jsem se připojil k laboratořím Art.com a stalo se, že jsem měl přesvědčivý případ použití pro model interakce „ve stylu iOS“ kolem vizuální navigace streamem obsahu – takový, který reagoval na různé velikosti obrazovky a vstupní paradigmata. (dotek, ukazatel, trackpad). Z této potřeby vzešel TremulaJS, komponenta uživatelského rozhraní Javascript určená k navigaci ve velkých sadách výsledků vizuálního obsahu s vysokou mírou plynulosti uživatelského rozhraní.

Tento článek se zaměřuje na to, jak je TremulaJS organizován, se zaměřením na vytváření responzivních, dlouhotrvajících animovaných interakcí pomocí Javascriptu.

*Pro zájemce o podrobný pohled na základy requestAnimationFrame() Implementace, Julian Shapiro, tvůrce velocity.js, toto téma stručně nadhodil tělem, aby vás potěšilo čtení zde . Považuji to za povinnou četbu pro každého, kdo se pouští do dobrodružství s animací JS.

Zobrazit DemoGet TremulaJS

TremulaJS:přehled komponent

TremulaJS se skládá z pěti hlavních komponent – ​​osy posouvání, smyčky hybnosti, mřížky obsahu, pole obsahu a projekce mřížky.

obr.1. Osa posouvání, posun posouvání a mřížka obsahu, jak se vztahují k kontejneru zobrazení TremulaJS. Tento obrázek ukazuje mřížku samostatných prvků obsahu, které se mohou posouvat (po ose) přes viditelnou oblast. Obsah mimo tuto oblast se nevykresluje.

Osa posuvu

TremulaJS umožňuje všechny druhy mikrointerakcí, nicméně na konci dne existuje pouze jedna dimenze navigace a tou je hodnota Scroll Offset. Tato hodnota je zapouzdřena objektem Scroll Axis, který mimo jiné řídí horizontální a vertikální orientaci.

Momentum Loop

Smyčka hybnosti reguluje hodnotu hybnosti v celém systému. Je to součet výstupů různých dílčích komponent, včetně:vnitřních hodin hybnosti, různých funkcí podmíněného tlumení spojených se stavy osy rolování a obsluhy události interakce uživatele. Na každém snímku animace vrací okamžitou výstupní hodnotu hybnosti použitou k výpočtu pozice posunu posunu.

Mřížka obsahu

Obsahová mřížka je abstraktní model obsahových boxů uspořádaných na konfigurovatelné mřížce XY. Veškerý obsah přidaný do této mřížky je proporcionálně upraven podél příčné osy, aby byly zachovány normalizované rozměry řádků (nebo sloupců).

V každém snímku, když hybnost přesune obsahovou mřížku do nové polohy podél osy posouvání, obsahová mřížka aktualizuje své podřízené obsahové rámečky novými relativními pozicemi. Toto je abstrakce, která nám dává příležitosti rozšířit proces malby a vytvořit skvělé věci…

Pole obsahu

Obsahové pole je vytvořeno pro každou jednotku obsahu připojenou k obsahové mřížce. Pole obsahu má šířku, výšku, volitelnou šablonu HTML a volitelný hlavní obrázek, který (pokud je k dispozici) je předem načten a převeden na obrazovku pomocí třídy CSS. Pro webového vývojáře by to nemělo být neznámé paradigma.

To zajímavé začíná zde: Každý blok obsahu také uchovává různé primitivní hodnoty tvaru vlny odpovídající jeho vlastnímu postupu posouvání na obrazovce. Tyto průběhy lze mapovat tak, aby animovaly jakýkoli aspekt prvku Content Box DOM v čase a prostoru. Pojďme si to přiblížit pomocí diagramu…

obr.2. Lineární průběh bloku obsahu přes obrazovku s „náběžnou“ křivkou zobrazenou pod ní.

Na obrázku výše můžeme sledovat blok obsahu, jak se pohybuje po obrazovce, a představit si, že výstup naší křivky rampy je mapován na funkci, která aktualizuje vlastnost CSS translateX().

Toto však není výchozí chování – je to trochu složitější. Zde je zjednodušený příklad výchozí funkce volané na Content Box během vykreslovacího cyklu…

function updateContentBoxElementProperites(x,y) {
  var ramp = this.waveforms.headRamp,
    xo=x,
    yo=y,
    zo=0;
    
  this.e.style.transform = 'translate3d(' + xo + 'px,' + yo +'px, ' + zo + 'px)';
  //this.e.style.opacity = ramp;
  this.pPos = [x,y];//cache the current position in the Content Box model
}

Tato funkce se volá, když je čas přemístit náš obsahový rámeček a zde vidíme, že jsou předány nové souřadnice. x &y jsou absolutní hodnoty odpovídající geometrii našeho pohledu TremulaJS, tyto hodnoty poskytuje funkci Content Grid, který má znalosti o všech blocích obsahu a je schopen efektivně rozdrtit všechny pozice všech polí obsahu na mřížce. . Výše uvedená funkce je pak volána na každém poli obsahu na každém snímku animace.

Všimněte si komentovaného přiřazení neprůhlednosti. Kdybychom to odkomentovali, viděli bychom, jak se náš blok obsahu zatmí, když se pohybuje zleva doprava (nebo zeslábne, když se pohybuje zprava doleva). Funguje to, protože naše hodnota rampy je odvozená hodnota (mezi 0 a 1) spojené s postupem posouvání Content Boxu v našem zobrazení TremulaJS. Pohodlně this.e.style.opacity očekává číslo mezi 0 a 1 .

News Flash:Ukázalo se, že Bézierovy cesty jsou super citlivé

Pohled na projekci mřížky

Existuje pátá složka patřící do TremulaJS, která nám umožňuje vzít prvky obsahové mřížky a promítat je po Bėzierově cestě. Není překvapením, že se tomu říká projekce mřížky .

Takže pro rekapitulaci: Jak je ukázáno v předchozím příkladu, díváme se na funkci Content Box, která se provádí na každém snímku. Této funkci jsou předány okamžité hodnoty x&y odpovídající vlastní orientaci Content Boxu v pohledu TremulaJS v konkrétním okamžiku. Této funkci je také předáno několik primitivních hodnot tvaru vlny odpovídající jejímu vlastnímu průběhu rolování na obrazovce. V tomto bodě jsme schopni přemapovat libovolnou Bezierovu cestu na prakticky jakoukoli vlastnost CSS. Podívejme se znovu na výše uvedený příklad, kromě toho, že změníme vertikální polohu našeho Content Boxu nahrazením absolutní pozice x&y polohou vygenerovanou naší Bézierovou funkcí.

obr.3. Lineární průběh bloku obsahu napříč pohledem s bézierovým průběhem zobrazeným pod ním. Bézierův výstup je nyní namapován na pozici x&y našeho obsahového pole v zobrazení TremulaJS.

var bezierArcPath = [
  {x:0,y:0},
  {x:0,y:1},
  {x:1,y:1},
  {x:1,y:0}
];

function updateContentBoxElementProperites(x,y,env) {

  var path = bezierArcPath;

  var 
    areaX = env.viewDims[0],
    areaY = env.viewDims[1],
    ramp = this.waveforms.tailRamp,
    xo=x,
    yo=y,
    zo=0;

  var xyFactor = [
    areaX,
    areaY
  ];

  var scaledPath = env.factorPathBy(path,xyFactor);
  
  var p = jsBezier.pointOnCurve(cubicBezier, ramp);
  var g = jsBezier.gradientAtPoint(cubicBezier, ramp);
  
  xo = p.x - (this.dims[0]*.5);
  yo = areaY - p.y - (this.dims[1]*.5);
  zo = 0;

  this.e.style.transform = 'translate3d(' + xo + 'px,' + yo +'px, ' + zo + 'px)';

  this.pPos = [x,y];
}

Upozorňujeme:Názvy proměnných v těchto příkladech byly změněny/vyčištěny, aby se zlepšilo porozumění na vysoké úrovni – skutečný kód není tak pěkný. Fork a zlepšit!

V tomto příkladu jsme přidali několik metod, které pomohou implementovat naše Bėzierovy transformace. Nejprve se podívejme na env.factorPathBy(path,xyFactor) . Síla odezvy této obslužné funkce je skvělá – umožňuje nám definovat jakoukoli oblast ohraničujícího rámečku (v tomto případě aktuální rozměry pohledu TremulaJS) a škálovat naši cestu ve dvou rozměrech tak, aby se cesta vešla do rámečku. To, co je vráceno, jsou předem nastavené souřadnice cesty připravené k použití.

Další v našem řetězci je jsBezier.pointOnCurve(cubicBezier, ramp) . Což bere naši škálovanou cestu a náš aktuální výstup rampy jako parametry. Naše transformované hodnoty x&y jsou vráceny. Velké díky patří Simonu Porrittovi za přenesení klasické Bėzierovy matematiky do JS a odeslání knihovny jsBezier na gitHub!

Zbytek by měl vypadat dostatečně povědomě. Poté provedeme drobné úpravy x&y, aby byl náš obsah umístěn od jeho středu.

Ale počkat, je toho víc! (Jen ne v tomto článku...)

Kromě tohoto příkladu existuje mnoho animací, které lze z těchto základních stavebních kamenů vytvořit. Například jsBezier.gradientAtPoint(cubicBezier, ramp) nám dává okamžité hodnoty tečny, jak se obsah pohybuje po naší cestě, což umožňuje koordinovanou rotaci obsahu mezi ostatními možnostmi. K dispozici je také osa z a primitivní trojúhelníkový tvar vlny, který umožňuje hloubkové efekty (přibližuje obsah, když se pohybuje do středu našeho pohledu).

Křivky lze stejně snadno použít k vytvoření efektů náběhu nebo k udržení našeho obsahu na jedné responzivně umístěné ose.

Další funkcí TremulaJS je hybnost Content Box. Je-li povoleno, obsahová mřížka neaktualizuje okamžitě DOM pole obsahu, když se změní posun posunu. Místo toho Content Box spravuje svou vlastní hodnotu hybnosti vzhledem k jejímu vztahu k umístění hybné síly (např. váš prst nebo ukazatel myši nad mřížkou) – to může vytvářet zajímavé efekty hybnosti na úrovni obsahu.

Zobrazit ukázku Získejte TremulaJS Experiment na CodePen

Pro ty, které to zajímá, je zde skvělý nástroj pro úpravu cesty...

https://www.desmos.com/calculator/d1ofwre0fr