Î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.:
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.
Î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;
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 Styler
pentru 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');
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 ();
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 start
ed, 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ă.
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ă asculta
. asculta
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);
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.
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!
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ă.
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)).
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.
Ce zici de a încerca aceste provocări: