Introducere în Popmotion scruber animație personalizat

În prima parte a seriei introductive Popmotion, am învățat cum să folosim time-based animații cum ar fi Tween și keyframes. De asemenea, am învățat cum să folosim aceste animații pe DOM, folosind performantele Styler.

În partea a doua, am învățat cum să folosim ac indicator urmărirea și înregistrarea viteză. Apoi, am folosit-o pentru a acționa pe bază de viteză- animații primăvarădescompunere, și fizică.

În această ultimă parte, vom crea un widget pentru scruber, și o vom folosi pentru ao curăța keyframes animaţie. Vom face widget-ul în sine dintr-o combinație de urmărire pointer, precum și primăvară și descompunere pentru a conferi o senzație mai viscerală decât scruber-urile de tip "run-of-the-mill".

Încercați-l pentru dvs.:

Noțiuni de bază

Marcaj

Mai întâi, introduceți acest cod CodePen pentru șablonul HTML. Ca și înainte, pentru că acesta este un tutorial intermediar, nu voi trece prin toate.

Principalul răsucire a notei este că mânerul de pe scruber este alcătuit din două div elemente: .mâner și .mâner-a lovit zona.

.mâner este indicatorul vizual albastru rotund al locului în care se află mânerul pentru scruber. Am împachetat-o ​​într-un element de zonă invizibilă pentru a face ca elementul să fie mai ușor pentru utilizatorii touchscreen.

Funcții de import

În partea de sus a panoului JS, importați tot ce vom folosi în acest tutorial:

const relaxare, cadre cheie, pointer, decădere, primăvară, stiler, transformare, ascultare, valoare = popmotion; const țeavă, clemă, condiționată, liniarăSpring, interpolată = transformare;

Selectați Elemente

Vom avea nevoie de trei elemente în acest tutorial. Vom anima .cutie, trageți și animați .mâner-a lovit zona, și măsurarea .gamă.

Să cream și noi Stylerpentru elementele pe care le vom anima:

const caseta = document.querySelector ("caseta"); const boxStyler = stiler (cutie); const mâner = document.querySelector ("handle-hit-area"); const mânerStyler = stiler (mâner); const domeniu = document.querySelector ('. range');

Animarea cadrelor cheie

Pentru animația noastră scrubbable, o să facem .cutie trecerea de la stânga la dreapta cu keyframes. Cu toate acestea, am putea la fel de usor scruti a Tween sau cronologia animație folosind aceeași metodă descrisă mai târziu în acest tutorial.

const boxAnimation = cadre cheie (valori: [0, -150, 150, 0], simplificări: [easing.backOut, easing.backOut, easing.easeOut], duration: 2000 ));

Animația dvs. va juca acum. Dar nu vrem asta! Să o oprim pentru moment:

boxAnimation.pause ();

Trageți axa x

E timpul să o folosiți ac indicator pentru a trage mânerul nostru de spălare. În tutorialul anterior, am folosit ambele X și y proprietati, dar cu un scruber avem nevoie doar X.

Preferăm să ne păstrăm codul reutilizabil și să urmărim un singur ac indicator axa este un caz destul de comun. Deci, să creăm o nouă funcție numită, imaginativ, pointerX.

Va funcționa exact așa ac indicator cu excepția faptului că va lua doar un singur număr ca argument și de ieșire doar un singur număr (X):

const pointerX = (x) => pointer (x) țeavă (xy => xy.x); 

Aici puteți vedea că folosim o metodă de ac indicator denumit țeavățeavă este disponibil pentru toate acțiunile Popmotion pe care le-am văzut până acum, inclusiv keyframes.

țeavă acceptă mai multe funcții. Când acțiunea este started, toată ieșirea va fi trecută prin fiecare dintre aceste funcții în schimb, înainte de Actualizați funcție oferită start incendii.

În acest caz, funcția noastră este pur și simplu:

xy => xy.x

Tot ce face este să ia  X y obiect obișnuit de ieșire de ac indicator și returnează doar X axă.

Ascultători de evenimente

Trebuie să știm dacă utilizatorul a început să apese mânerul înainte de a începe să urmărim cu noul nostru pointerX funcţie.

În ultimul tutorial am folosit tradiționalul addEventListener funcţie. De data aceasta, vom folosi o altă funcție Popmotion numită ascultaasculta oferă, de asemenea, o țeavă precum și accesul la toate metodele de acțiune, dar nu o vom folosi aici.

asculta ne permite să adăugăm ascultători de evenimente la mai multe evenimente cu o singură funcție, similară cu jQuery. Așadar, putem condensa cei patru ascultători ai evenimentelor anterioare la două:

ascultați (mâner, "touch-start mousedown"). start (startDrag); ascultați (document, 'mouseup touchend'). start (stopDrag);

Deplasați mânerul

Vom avea nevoie de mânerul x viteză mai târziu, deci hai să facem asta valoare, care, așa cum am învățat în ultimul tutorial ne permite să întrebăm viteza. Pe linia după ce definim handleStyler, adăuga:

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

Acum ne putem adăuga startDrag și stopDrag funcţii:

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

În acest moment, mânerul poate fi curățat dincolo de limitele glisorului, dar vom reveni la acest lucru mai târziu.

de spălare a gazelor

Acum avem un scruber vizual funcțional, dar nu curățim animația reală.

Fiecare valoare are o Abonati-va metodă. Acest lucru ne permite să atașăm mai mulți abonați la foc atunci când valoare schimbări. Vrem să căutăm keyframes animație ori de câte ori handleX actualizări.

Mai întâi, măsurați cursorul. Pe linia după ce definim gamă, adăuga:

const limităWidth = range.getBoundingClientRect () width;

keyframes.seek acceptă o valoare progresivă exprimată din 0 la 1, în timp ce noi handleX este setat cu valorile pixelilor de la 0 la rangeWidth.

Putem converti de la măsurarea pixelilor la a 0 la 1 interval prin împărțirea măsurării pixelilor curente cu rangeWidth. Pe linia după boxAnimation.pause (), adăugați această metodă de abonare:

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

Acum, dacă joci cu scruber, animația se va curăța cu succes!

Extra Mile

Boundaries de primăvară

Scruberul poate fi în continuare tras în afara limitelor întregii game. Pentru a rezolva asta, noi ar putea utilizați pur și simplu a clemă pentru a ne asigura ca nu vom iesi in afara valorilor 0, rangeWidth.

În schimb, vom merge la pasul suplimentar și vom atașa izvoare până la capătul cursorului nostru. Când un utilizator trage mânerul dincolo de intervalul permis, se va trage înapoi spre el. Dacă utilizatorul eliberează mânerul în timp ce se află în afara intervalului, putem folosi a primăvară animație pentru ao prinde înapoi.

Vom face acest proces o singură funcție pe care o putem oferi pointerX țeavă metodă. Prin crearea unei funcții unice, reutilizabile, putem reuși să folosim această bucată de cod cu orice animație Popmotion, cu intervale configurabile și rezistențe la primăvară.

Mai întâi, să aplicăm un izvor la limita cea mai stângă. Vom folosi două transformatoare, condițional și linearSpring.

const springRange = (min, max, forță) => condițional (v => v < min, linearSpring(strength, min) );

condițional are două funcții, o afirmație și un transformator. Afirmația primește valoarea furnizată și returnează fie ea Adevărat sau fals. Dacă se întoarce Adevărat, la a doua funcție va fi oferită valoarea de transformare și returnare.

În acest caz, afirmația spune: "Dacă valoarea furnizată este mai mică decât min, treceți această valoare prin linearSpring transformator " linearSpring este o funcție simplă de primăvară care, spre deosebire de fizică sau primăvară animații, nu are concept de timp. Asigurați-o a putere și a ţintă, și va crea o funcție care "atrage" orice valoare dată spre țintă cu puterea definită.

Înlocuiți-ne startDrag funcția cu aceasta:

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

Acum trecem cu pointerul X offset prin intermediul nostru springRange astfel încât dacă trageți mânerul dincolo de partea cea mai din stânga, veți observa remorcarea acestuia.

Aplicarea aceluiași în partea dreaptă este o chestiune de a compune o secundă condițional cu prima folosind stand-alone țeavă funcţie:

const springRange = (min, max, puterea) => țeavă (condiționată (v => v < min, linearSpring(strength, min) ), conditional( v => v> max, linearSpring (putere, max)));

Un alt beneficiu al compunerii unei funcții cum ar fi springRange este că devine foarte testabil. Funcția pe care o returnează este, ca toate transformatoarele, o funcție pură care ia o singură valoare. Puteți testa această funcție pentru a vedea dacă trece prin valori care se află înăuntru min și max nealterată, și dacă aplică izvoare la valori care nu există.

Dacă eliberați mânerul în timp ce acesta se află în afara intervalului, acesta ar trebui să revină înapoi în intervalul de timp. Pentru asta, va trebui să ajustăm stopDrag funcția de foc a primăvară animaţie:

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

Al nostru snapHandleToEnd funcționează astfel:

const snapHandleToEnd = (x) => arc (de la: x, viteza: handleX.getVelocity (), la: x < 0 ? 0 : rangeWidth, damping: 30, stiffness: 5000 ).start(handleX);

Puteți vedea asta la este setat fie ca 0 sau rangeWidth în funcție de ce parte a cursorului se află mânerul. Jucând cu amortizare și rigiditate, puteți juca cu o serie de diferite simțuri de primăvară.

Momentul de derulare

O atingere plăcută pe scruber-ul iOS pe care l-am apreciat mereu era că, dacă arunci mânerul, ar încetini încet încet, mai degrabă decât să se oprească. Putem replica acest lucru cu ușurință folosind descompunere animaţie.

În stopDrag, a inlocui handleX.stop () cu momentumScroll (x).

Apoi, pe linia după snapHandleToEnd funcția, adăugați o nouă funcție numită momentumScroll:

const momentumScroll = (x) => dezintegrare (de la: x, viteza: handleX.getVelocity ()) start (handleX);

Acum, dacă arunci mânerul, se va opri treptat. Acesta va anima, de asemenea, în afara intervalului de cursor. Putem opri acest lucru prin trecerea lui clemă transformator la decay.pipe metodă:

const momentumScroll = (x) => dezintegrare (din: x, viteza: handleX.getVelocity ()) țeavă (clamp (0, rangeWidth)).

Concluzie

Folosind o combinație de funcții Popmotion diferite, putem crea un scruber care are puțin mai multă viață și mai joacă decât cel obișnuit.

Prin utilizarea țeavă, compunem simple funcții pure în comportamente mai complexe, lăsând componentele compozite testabile și reutilizabile.

Pasii urmatori

Ce zici de a încerca aceste provocări:

  • Asigurați-vă că capătul de derulare a impulsului cu o mișcare dacă mânerul atinge un capăt al scruber-ului.
  • Asigurați-mâner animați în orice punct de pe scruber atunci când un utilizator face clic pe o altă parte a barei de interval.
  • Adăugați comenzi complete de redare, cum ar fi un buton de redare / pauză. Actualizați poziția mânerului scruberului pe măsură ce progresează animația.
Cod