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år
, fö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:
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.
Ö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;
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 styler
s 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');
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 ();
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ör
. rör
finns tillgänglig på alla Popmotion-åtgärder vi hittills hittat, inklusive nyckelrutor
.
rör
accepterar flera funktioner. När åtgärden är Start
ed, 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.
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 lyssna
. lyssna
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);
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.
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!
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.
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.
I 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);
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.
Vad sägs om att prova dessa utmaningar: