Introduksjon til Popmotion Custom Animation Scrubber

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årforfall, 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:

Starter

Markup

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.

Import Funksjoner

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;

Velg elementer

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 stylers 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');

Keyframes Animasjon

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 ();

Dra x-aksen

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ørrør er tilgjengelig på alle Popmotion-handlingene vi har sett så langt, inkludert nøkkelbilder.

rør aksepterer flere funksjoner. Når handlingen er started, 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.

Event Lyttere

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 lyttelytte 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);

Flytt håndtaket

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.

skrubbing

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!

The Extra Mile

Vårgrenser

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.

Momentum Scrolling

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.

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);

Konklusjon

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.

Neste skritt

Hva med å prøve disse utfordringene:

  • Gjør momentet rullende ende med en sprette hvis håndtaket treffer hver ende av skrubberen.
  • Gjør håndtaket til et hvilket som helst punkt på skrubberen når en bruker klikker på en annen del av intervalllinjen.
  • Legg til full avspillingskontroller, som en avspilling / pause-knapp. Oppdater skrubberhåndtakets posisjon når animasjonen utvikler seg.