I den første delen av Popmotions introduksjonsserie lærte vi å bruke tidsbasert animasjoner som tween
og nøkkelbilder
. Vi lærte også å bruke disse animasjonene på DOM, ved hjelp av performant styler
.
I del to lærte vi å bruke pekeren
sporing og registrering hastighet
. Vi brukte da det til å drive hastighetsbaserte animasjoner vår
, forfall
, og fysikk
.
I denne siste delen skal vi lage en scrubber-widget, og vi skal bruke den til å skrubbe en nøkkelbilder
animasjon. Vi vil lage widgeten selv fra en kombinasjon av peker sporing så vel som vår
og forfall
for å gi den en mer visceral følelse enn rennende skrubber.
Prøv det selv:
Forgaffel denne kodenPen for HTML-malen. Som før, fordi dette er en mellomveiledning, vil jeg ikke gå gjennom alt.
Hovedpunktet er at håndtaket på skrubberen består av to div
elementer: .håndtak
og .håndtere-hit-området
.
.håndtak
er den runde blå visuelle indikatoren for hvor skrubbehåndtaket er. Vi har pakket det inn i et usynlig hitelementelement for å få tak i elementet for berøringsskjermbrukere.
På toppen av JS-panelet ditt, importerer alt vi skal bruke i denne opplæringen:
const easing, keyframes, peker, forfall, vår, styler, transformer, lytt, verdi = popmotion; const pipe, clamp, conditional, linearSpring, interpolate = transform;
Vi trenger tre elementer i denne opplæringen. Vi vil animere .eske
, dra og animere .håndtere-hit-området
, og måle .område
.
La oss også lage styler
s for elementene vi skal animere:
const box = document.querySelector ('. box'); const boxStyler = styler (boks); const handle = document.querySelector ('. handle-hit-area'); const handleStyler = styler (håndtak); const range = document.querySelector ('. range');
For vår skrubbe animasjon, skal vi lage .eske
Flytt fra venstre til høyre med nøkkelbilder
. Imidlertid kan vi like enkelt skrubbe en tween
eller tidslinje
animasjon ved hjelp av samme metode som er skissert senere i denne opplæringen.
const boxAnimation = keyframes (verdier: [0, -150, 150, 0], easings: [easing.backOut, easing.backOut, easing.easeOut], varighet: 2000). start (boxStyler.set ('x' ));
Din animasjon vil nå spilles. Men vi vil ikke ha det! La oss pause det for nå:
boxAnimation.pause ();
Det er på tide å bruke pekeren
å dra vår skrubbehåndtak. I den tidligere opplæringen brukte vi begge x
og y
egenskaper, men med en skrubber trenger vi bare x
.
Vi foretrekker å holde koden gjenbrukbar og spore en enkelt pekeren
akse er ganske vanlig bruk saken. Så la oss lage en ny funksjon som kalles, fantasifullt, pointerX
.
Det vil fungere akkurat som pekeren
bortsett fra at det tar bare et enkelt nummer som argument og utfører bare et enkelt nummer (x
):
const pointerX = (x) => peker (x) .rør (xy => xy.x);
Her kan du se at vi bruker en metode for pekeren
kalt rør
. rør
er tilgjengelig på alle Popmotion-handlingene vi har sett så langt, inkludert nøkkelbilder
.
rør
aksepterer flere funksjoner. Når handlingen er start
ed, vil alle utgangene passere gjennom hver av disse funksjonene i sin tur før Oppdater
funksjon levert til start
branner.
I dette tilfellet er vår funksjon ganske enkelt:
xy => xy.x
Alt det gjør er å ta x, y
Objektet sendes vanligvis ut av pekeren
og returnerer bare x
akser.
Vi må vite om brukeren har begynt å trykke på håndtaket før vi begynner å spore med vårt nye pointerX
funksjon.
I den siste opplæringen brukte vi den tradisjonelle addEventListener
funksjon. Denne gangen skal vi bruke en annen Popmotion-funksjon kalt lytte
. lytte
gir også en rør
metode, samt tilgang til alle handlingsmetoder, men vi skal ikke bruke det her.
lytte
lar oss legge til hendelseslyttere til flere hendelser med en enkelt funksjon, likt jQuery. Så vi kan kondensere de fire foregående hendelsene til to:
lytt (håndtere, 'mousedown touchstart'). start (startDrag); lytt (dokument, 'mouseup touchend'). start (stopDrag);
Vi vil trenge håndtakets x hastighet
senere, så la oss gjøre det til en verdi
, som vi lærte i den siste opplæringen, lar oss spørre hastighet. På linjen etter at vi har definert handleStyler
, Legg til:
const handleX = verdi (0, handleStyler.set ('x'));
Nå kan vi legge til vår startdrag
og stopdrag
funksjoner:
const startDrag = () => pointerX (handleX.get ()) .start (handleX); const stopDrag = () => handleX.stop ();
For øyeblikket kan håndtaket skrubbe utover grensene til skyvekontrollen, men vi kommer tilbake til dette senere.
Nå har vi en visuelt funksjonell skrubber, men vi skyr ikke selve animasjonen.
Hver verdi
har en abonnere
metode. Dette tillater oss å knytte flere abonnenter til brann når verdi
Endringer. Vi ønsker å søke nøkkelbilder
animasjon når som helst handleX
oppdateringer.
Først måler glidebryteren. På linjen etter at vi har definert område
, Legg til:
const rangeWidth = range.getBoundingClientRect (). bredde;
keyframes.seek
aksepterer en fremdriftsverdi som uttrykt fra 0
til 1
, mens vår handleX
er satt med pixelverdier fra 0
til rangeWidth
.
Vi kan konvertere fra pikselmåling til a 0
til 1
rekkevidde ved å dele den nåværende pixelmåling med rangeWidth
. På linjen etter boxAnimation.pause ()
, legg til denne abonnementsmetoden:
handleX.subscribe (v => boxAnimation.seek (v / rangeWidth));
Nå, hvis du spiller med skrubberen, vil animasjonen skrubbe vellykket!
Skrubbemaskinen kan fortsatt trekkes utenfor grensene for hele spekteret. For å løse dette, vi kunne bare bruk a klemme
funksjon for å sikre at vi ikke utfører verdier utenfor 0, områdebredde
.
I stedet skal vi gå ekstra trinn og feste fjærer til slutten av skyveknappen. Når en bruker trekker håndtaket utover det tillatte området, vil den trekke seg mot den. Hvis brukeren slipper håndtaket mens det er utenfor området, kan vi bruke a vår
animasjon for å snappe den tilbake.
Vi vil gjøre denne prosessen til en enkelt funksjon som vi kan gi til pointerX
rør
metode. Ved å lage en enkelt gjenbrukbar funksjon, kan vi gjenbruke dette stykke kode med hvilken som helst Popmotion-animasjon, med konfigurerbare områder og vårstyrker.
Først, la oss søke en kilde til venstre grense. Vi bruker to transformatorer, betinget
og linearSpring
.
const springRange = (min, maks, styrke) => betinget (v => v < min, linearSpring(strength, min) );
betinget
tar to funksjoner, en påstand og en transformator. Påstanden mottar den angitte verdien og returnerer heller ekte
eller falsk
. Hvis den returnerer ekte
, den andre funksjonen vil bli gitt verdien til å transformere og returnere.
I dette tilfellet sier påstanden, "Hvis den angitte verdien er mindre enn min
, send denne verdien gjennom linearSpring
transformator. "The linearSpring
er en enkel vårfunksjon som, i motsetning til fysikk
eller vår
animasjoner, har ikke tidskonsept. Gi det en styrke
og a mål
, og det vil skape en funksjon som "tiltrekker" en gitt verdi mot målet med den definerte styrken.
Erstatt vår startdrag
fungere med dette:
const startDrag = () => pointerX (handleX.get ()) pipe (springRange (0, rangeWidth, 0.1)) .start (handleX);
Vi passerer nå pekeren x
kompensert gjennom vår springRange
funksjon, så hvis du drar håndtaket forbi venstre side, vil du legge merke til at det er trukket tilbake.
Å bruke det samme til høyre side er et spørsmål om å komponere et sekund betinget
med den første bruker frittstående rør
funksjon:
const springRange = (min, maks, styrke) => rør (betinget (v => v < min, linearSpring(strength, min) ), conditional( v => v> maks, lineærspring (styrke, maks)));
En annen fordel med å komponere en funksjon som springRange
er at det blir veldig testbart. Funksjonen som returnerer er, som alle transformatorer, en ren funksjon som tar en enkelt verdi. Du kan teste denne funksjonen for å se om den går gjennom verdier som ligger innenfor min
og max
uendret, og hvis det gjelder fjærer til verdier som ligger uten.
Hvis du slipper håndtaket mens det ligger utenfor området, bør det nå komme tilbake til innen rekkevidde. For det må vi justere stopdrag
funksjon for å brenne a vår
animasjon:
const stopDrag = () => const x = handleX.get (); (x < 0 || x > rangeWidth)? snapHandleToEnd (x): handleX.stop (); ;
Våre snapHandleToEnd
funksjonen ser slik ut:
const snapHandleToEnd = (x) => våren (fra: x, hastighet: handleX.getVelocity (), til: x < 0 ? 0 : rangeWidth, damping: 30, stiffness: 5000 ).start(handleX);
Du kan se det til
er satt enten som 0
eller rangeWidth
avhengig av hvilken side av glidebryteren håndtaket sitter i øyeblikket. Ved å leke med demping
og stivhet
, Du kan spille med en rekke forskjellige vårføler.
En fin touch på IOS scrubber som jeg alltid verdsatt var at hvis du kastet håndtaket, ville det gradvis sakte i stedet for å komme til en død stopp. Vi kan gjenskape det enkelt ved å bruke forfall
animasjon.
I stopdrag
, erstatte handleX.stop ()
med momentumScroll (x)
.
Deretter på linjen etter snapHandleToEnd
funksjon, legg til en ny funksjon kalt momentumScroll
:
const momentumScroll = (x) => forfall (fra: x, hastighet: handleX.getVelocity ()). start (handleX);
Nå, hvis du kaster håndtaket, kommer det til en gradvis stopp. Det vil også animere utenfor rekkevidde av skyveknappen. Vi kan stoppe dette ved å passere klemme
transformator til decay.pipe
metode:
const momentumScroll = (x) => forfall (fra: x, hastighet: handleX.getVelocity ()). rør (klemme (0, områdevidde)) .start (handleX);
Ved å bruke en kombinasjon av forskjellige Popmotion-funksjoner, kan vi lage en skrubber som har litt mer liv og lekskap enn det vanlige.
Ved bruk av rør
, vi komponerer enkle rene funksjoner i mer komplekse oppføringer, mens de komposittbrikkene blir testbare og gjenbrukbare.
Hva med å prøve disse utfordringene: