Introduktion till Popmotion Custom Animation Scrubber

I den första delen av Popmotions introduktionsserie lärde vi oss att använda tids baserad animeringar som tween och nyckelrutor. Vi lärde oss också att använda de animationerna på DOM, med hjälp av performanten styler.

I del två lärde vi oss att använda pekare spårning och inspelning hastighet. Vi använde det för att driva hastighetsbaserade animationer vårförfall, och fysik.

I den här sista delen kommer vi att skapa en scrubber-widget, och vi ska använda den för att skrubba en nyckelrutor animering. Vi gör widgeten själv från en kombination av pekarspårning såväl som vår och förfall för att ge den en mer visceral känsla än avlöpningsskrubbare.

Prova själv:

Komma igång

Markup

Först, gaffel denna CodePen för HTML-mallen. Som tidigare, eftersom det här är en mellanhandledning, går jag inte igenom allt.

Den viktigaste vridningen är att handtaget på skrubben består av två div element: .hantera och .handtag-hit-område.

.hantera är den runda blå visuella indikatorn på var handskruvhandtaget är. Vi har lindat det i ett osynligt träffområde för att fånga elementet lättare för pekskärmsanvändare.

Importera funktioner

Överst på din JS-panel, importera allt vi ska använda i denna handledning:

const easing, keyframes, pekare, förfall, vår, styler, transformera, lyssna, värde = popmotion; const pipe, clamp, conditional, linearSpring, interpolate = transform;

Välj element

Vi behöver tre element i denna handledning. Vi kommer att animera .låda, dra och animera .handtag-hit-område, och mäta .räckvidd.

Låt oss också skapa stylers för de element vi ska animera:

const box = document.querySelector ('. box'); const boxStyler = styler (box); const handtag = document.querySelector ('.handtag-hit-område'); const handleStyler = styler (handtag); const range = document.querySelector ('. range');

Keyframes Animation

För vår skurbara animation ska vi göra .låda Flytta från vänster till höger med nyckelrutor. Vi kan dock lika enkelt skrubba en tween eller tidslinje animering med samma metod som skisseras senare i denna handledning.

const boxAnimation = keyframes (värden: [0, -150, 150, 0], easings: [easing.backOut, easing.backOut, easing.easeOut], duration: 2000). start (boxStyler.set ('x' ));

Din animering spelas nu. Men vi vill inte ha det! Låt oss pausa det för tillfället:

boxAnimation.pause ();

Dra x-axeln

Det är dags att använda pekare att dra vårt skrubbehandtag. I den tidigare handledningen använde vi båda x och y egenskaper, men med en skrubber behöver vi bara x.

Vi föredrar att hålla vår kod återanvändbar och spåra en enda pekare axeln är ganska vanligt bruk fallet. Så låt oss skapa en ny funktion som kallas, fantasifullt, pointerX.

Det kommer att fungera exakt som pekare förutom att det bara tar ett enda nummer som sitt argument och skriver ut ett enda nummer (x):

const pointerX = (x) => pekare (x). rör (xy => xy.x); 

Här kan du se att vi använder en metod för pekare kallad rörrör finns tillgänglig på alla Popmotion-åtgärder vi hittills hittat, inklusive nyckelrutor.

rör accepterar flera funktioner. När åtgärden är Started, kommer all utmatning att passera genom var och en av dessa funktioner i sin tur före uppdatering funktion som tillhandahålls till Start bränder.

I så fall är vår funktion helt enkelt:

xy => xy.x

Allt det gör är att ta x, y objekt som vanligtvis produceras av pekare och återvänder bara x axel.

Händelsemedlemmar

Vi behöver veta om användaren har börjat trycka på handtaget innan vi börjar spåra med vårt nya pointerX fungera.

I den sista handledningen använde vi den traditionella addeventlistener fungera. Den här gången kommer vi att använda en annan Popmotion-funktion som heter lyssnalyssna ger också a rör metod, samt tillgång till alla åtgärdsmetoder, men vi kommer inte att använda det här.

lyssna tillåter oss att lägga till händelse lyssnare i flera händelser med en enda funktion, som liknar jQuery. Så vi kan kondensera de föregående fyra händelselyttarna till två:

lyssna (hantera, "mousedown touchstart"). start (startDrag); lyssna (dokument, "mouseup touchend"). start (stopDrag);

Flytta handtaget

Vi behöver handtagets x hastighet senare, så låt oss göra det en värde, vilket som vi lärde oss i den sista handledningen gör att vi kan fråga hastighet. På linjen efter att vi definierat handleStyler, Lägg till:

const handleX = value (0, handleStyler.set ('x'));

Nu kan vi lägga till vårt startdrag och stopdrag funktioner:

const startDrag = () => pointerX (handleX.get ()) .start (handtagX); const stopDrag = () => handleX.stop ();

Just nu kan handtaget skruvas bort förbi reglaget, men vi kommer tillbaka senare.

Skur

Nu har vi en visuellt funktionell skrubber, men vi skrubbar inte själva animationen.

Varje värde har en prenumerera metod. Detta gör det möjligt för oss att bifoga flera abonnenter att skjuta när värde förändringar. Vi vill söka nyckelrutor animering när som helst handleX uppdateringar.

Mät först reglaget. På linjen efter att vi definierat räckvidd, Lägg till:

const rangeWidth = range.getBoundingClientRect (). bredd;

keyframes.seek accepterar ett utvecklingsvärde som uttryckt från 0 till 1, medan vår handleX är inställd med pixelvärden från 0 till rangeWidth.

Vi kan konvertera från pixelmätningen till a 0 till 1 intervall genom att dividera den aktuella pixelmätningen med rangeWidth. På linjen efter boxAnimation.pause (), lägg till den här prenumerationsmetoden:

handleX.subscribe (v => boxAnimation.seek (v / rangeWidth));

Nu, om du spelar med skrubbern, kommer animationen att skrubba framgångsrikt!

The Extra Mile

Vårgränser

Skrubbern kan fortfarande dras utanför gränserna för hela sortimentet. För att lösa detta, vi skulle kunna använd helt enkelt a klämma funktion för att säkerställa att vi inte matar ut värden utanför 0, rangeWidth.

I stället ska vi gå extra steg och bifoga fjädrar till slutet av vår skjutreglage. När en användare drar handtaget utöver det tillåtna intervallet, kommer det att dra tillbaka mot det. Om användaren släpper handtaget medan den ligger utanför intervallet kan vi använda en vår animering för att fånga den tillbaka.

Vi gör denna process till en enda funktion som vi kan ge till pointerX rör metod. Genom att skapa en enda återanvändbar funktion kan vi återanvända denna kod med alla Popmotion-animationer, med konfigurerbara intervall och vårstyrkor.

Låt oss först tillämpa en fjäder till vänstra gränsen. Vi använder två transformatorer, villkorlig och linearSpring.

const springRange = (min, max, styrka) => villkorlig (v => v < min, linearSpring(strength, min) );

villkorlig tar två funktioner, en påstående och en transformator. Påståendet mottar det angivna värdet och returnerar heller Sann eller falsk. Om den återvänder Sann, Den andra funktionen kommer att tillhandahållas värdet för att transformera och återvända.

I detta fall säger påståendet "Om det angivna värdet är mindre än min, Överför detta värde genom linearSpring transformator. " linearSpring är en enkel fjäderfunktion som, till skillnad från fysik eller vår animationer, har inget begrepp av tid. Ge det en styrka och a mål, och det kommer att skapa en funktion som "lockar" ett visst värde mot målet med den definierade styrkan.

Byt ut vår startdrag funktion med detta:

const startDrag = () => pointerX (handleX.get ()) pip (springRange (0, rangeWidth, 0,1)) .start (handleX);

Vi passerar nu pekaren x kompenseras genom vår springRange funktion, så om du drar handtaget förbi den vänstra sidan, kommer du att märka det till backar.

Att tillämpa samma till höger sida är en fråga om att komponera en sekund villkorlig med den första som använder fristående rör fungera:

const springRange = (min, max, styrka) => rör (villkorlig (v => v < min, linearSpring(strength, min) ), conditional( v => v> max, linjärspring (styrka, max)));

En annan fördel med att komponera en funktion som springRange är att det blir mycket testbart. Funktionen som den returnerar är som alla transformatorer, en ren funktion som tar ett enda värde. Du kan testa denna funktion för att se om den passerar genom värden som ligger inom min och max oförändrad, och om det gäller fjädrar till värden som ligger utan.

Om du släpper handtaget medan det ligger utanför intervallet, ska det nu springas tillbaka inom intervallet. För det måste vi justera stopdrag funktion att skjuta a vår animering:

const stopDrag = () => const x = handleX.get (); (x < 0 || x > rangeWidth)? snapHandleToEnd (x): handleX.stop (); ;

Vår snapHandleToEnd funktionen ser så här ut:

const snapHandleToEnd = (x) => fjäder (från: x, hastighet: handleX.getVelocity (), till: x < 0 ? 0 : rangeWidth, damping: 30, stiffness: 5000 ).start(handleX);

Du kan se det till är inställd antingen som 0 eller rangeWidth beroende på vilken sida av skjutreglaget handtaget sitter för närvarande. Genom att leka med dämpning och styvhet, du kan spela med olika spring-känslor.

Momentum Scrolling

En fin touch på IOS-skrubber som jag alltid uppskattat var att om du kastade handtaget, skulle det gradvis sakta ner än att komma till ett dödstopp. Vi kan replikera det enkelt med hjälp av förfall animering.

stopdrag, byta ut handleX.stop () med momentumScroll (x).

Sedan på linjen efter snapHandleToEnd funktion, lägg till en ny funktion som heter momentumScroll:

const momentumScroll = (x) => förfall (från: x, hastighet: handleX.getVelocity ()). start (handtagX);

Nu, om du kasta handtaget kommer det att komma till ett gradvis stopp. Det kommer också att animera utanför reglaget. Vi kan stoppa detta genom att passera klämma transformator till decay.pipe metod:

const momentumScroll = (x) => förfall (från: x, hastighet: handtagX.getVelocity ()). rör (klämma (0, rangeWidth)) .start (handtagX);

Slutsats

Med hjälp av en kombination av olika Popmotion-funktioner kan vi skapa en skrubber som har lite mer liv och lekfullhet än det vanliga.

Genom att använda rör, vi sammanställer enkla rena funktioner till mer komplexa beteenden samtidigt som de kompositbitarna kan testas och återanvändas.

Nästa steg

Vad sägs om att prova dessa utmaningar:

  • Gör momentumrulländen med en studsa om handtaget träffar någondera änden av skrubbern.
  • Gör handtaget animerat till någon punkt på skrubbern när en användare klickar på en annan del av intervallraden.
  • Lägg till hela spelkontrollen, som en uppspelning / pausknapp. Uppdatera läget för skrubberhandtaget när animationen fortskrider.