Nella prima parte della serie introduttiva di Popmotion, abbiamo imparato a usare basato sul tempo animazioni come gemello
e i fotogrammi chiave
. Abbiamo anche imparato come usare quelle animazioni sul DOM, usando il performante styler
.
Nella seconda parte, abbiamo imparato come usare pointer
tracciamento e registrazione velocità
. Abbiamo quindi usato quello per alimentare il velocità basata animazioni primavera
, decadimento
, e fisica
.
In questa parte finale, creeremo un widget di scrubber e lo useremo per scrub a i fotogrammi chiave
animazione. Creeremo il widget stesso da una combinazione di puntatore e tracciamento primavera
e decadimento
per dargli un aspetto più viscerale degli scrubber run-of-the-mill.
Provalo tu stesso:
Innanzitutto, imposta questo CodePen per il modello HTML. Come prima, perché questo è un tutorial intermedio, non passerò attraverso tutto.
La nota principale è che il manico della lavasciuga è composto da due div
elementi: .maniglia
e .gestire-hit-zona
.
.maniglia
è l'indicatore visivo rotondo blu di dove si trova la maniglia dello scrubber. L'abbiamo avvolto in un invisibile elemento dell'area colpita per rendere più semplice l'elemento per gli utenti touchscreen.
Nella parte superiore del pannello JS, importa tutto ciò che useremo in questo tutorial:
const easing, keyframes, pointer, decay, spring, styler, transform, ascolta, value = popmotion; const pipe, clamp, condizionale, linearSpring, interpolate = transform;
Avremo bisogno di tre elementi in questo tutorial. Animeremo il .scatola
, trascina e anima il .gestire-hit-zona
, e misurare il .gamma
.
Creiamo anche styler
s per gli elementi che animeremo:
const box = document.querySelector ('. box'); const boxStyler = styler (box); const handle = document.querySelector ('. handle-hit-area'); const handleStyler = styler (handle); const range = document.querySelector ('. range');
Per la nostra animazione scrubbable, faremo il .scatola
sposta da sinistra a destra con i fotogrammi chiave
. Tuttavia, potremmo altrettanto scrub a gemello
o sequenza temporale
animazione utilizzando lo stesso metodo descritto più avanti in questo tutorial.
const boxAnimation = keyframes (values: [0, -150, 150, 0], easings: [easing.backOut, easing.backOut, easing.easeOut], durata: 2000). start (boxStyler.set ('x' ));
La tua animazione sarà ora in riproduzione. Ma non lo vogliamo! Facciamo una pausa per ora:
boxAnimation.pause ();
È il momento di usare pointer
per trascinare la maniglia del dispositivo di scorrimento. Nel precedente tutorial, abbiamo usato entrambi X
e y
proprietà, ma con uno scrubber di cui abbiamo bisogno X
.
Preferiamo mantenere il nostro codice riusabile e tracciare un singolo pointer
l'asse è un caso d'uso abbastanza comune. Creiamo quindi una nuova funzione chiamata, in modo fantasioso, pointerX
.
Funzionerà esattamente come pointer
tranne che prenderà solo un singolo numero come argomento e produrrà solo un singolo numero (X
):
const pointerX = (x) => puntatore (x). pipe (xy => xy.x);
Qui, puoi vedere che stiamo usando un metodo di pointer
chiamato tubo
. tubo
è disponibile su tutte le azioni di Popmotion che abbiamo visto finora, incluso i fotogrammi chiave
.
tubo
accetta più funzioni. Quando l'azione è inizio
ed, tutti gli output saranno passati attraverso ciascuna di queste funzioni a turno, prima del aggiornare
funzione fornita a inizio
incendi.
In questo caso, la nostra funzione è semplicemente:
xy => xy.x
Tutto ciò che sta facendo è prendere il x, y
oggetto di solito prodotto da pointer
e restituendo solo il X
asse.
Dobbiamo sapere se l'utente ha iniziato a premere l'handle prima di iniziare a monitorare con il nostro nuovo pointerX
funzione.
Nell'ultimo tutorial abbiamo usato il tradizionale addEventListener
funzione. Questa volta, useremo un'altra funzione di Popmotion chiamata ascolta
. ascolta
fornisce anche a tubo
metodo, così come l'accesso a tutti i metodi di azione, ma non lo useremo qui.
ascolta
ci consente di aggiungere listener di eventi a più eventi con un'unica funzione, simile a jQuery. Quindi possiamo condensare i precedenti quattro ascoltatori di eventi a due:
ascolta (maneggia, 'mouse startstart'). start (startDrag); ascolta (documento, 'mouseup touchend'). start (stopDrag);
Avremo bisogno della x del manico velocità
più avanti, quindi facciamolo un valore
, che come abbiamo appreso nell'ultimo tutorial ci consente di interrogare la velocità. Sulla linea dopo aver definito handleStyler
, Inserisci:
const handleX = value (0, handleStyler.set ('x'));
Ora possiamo aggiungere il nostro startDrag
e stopDrag
funzioni:
const startDrag = () => pointerX (handleX.get ()) .start (handleX); const stopDrag = () => handleX.stop ();
In questo momento, il manico può essere rimosso oltre i limiti del cursore, ma torneremo su questo più tardi.
Ora abbiamo uno scrubber visivamente funzionale, ma non stiamo scrubbing l'animazione attuale.
Ogni valore
ha un sottoscrivi
metodo. Questo ci permette di collegare più abbonati a sparare quando il valore
i cambiamenti. Vogliamo cercare il i fotogrammi chiave
animazione ogni volta HANDLEX
aggiornamenti.
Innanzitutto, misura il cursore. Sulla linea dopo aver definito gamma
, Inserisci:
const rangeWidth = range.getBoundingClientRect (). width;
keyframes.seek
accetta un valore di avanzamento come espresso da 0
a 1
, mentre il nostro HANDLEX
è impostato con valori di pixel da 0
a rangeWidth
.
Possiamo convertire dalla misurazione dei pixel in a 0
a 1
intervallo dividendo la misura del pixel corrente per rangeWidth
. Sulla linea dopo boxAnimation.pause ()
, aggiungi questo metodo di iscrizione:
handleX.subscribe (v => boxAnimation.seek (v / rangeWidth));
Ora, se giochi con lo scrubber, l'animazione si scrarrà correttamente!
Lo scrubber può ancora essere tirato fuori dai limiti dell'intera gamma. Per risolvere questo, noi poteva usa semplicemente a morsetto
funzione per garantire che non vengano emessi valori al di fuori di 0, rangeWidth
.
Invece, faremo il passo in più e attacceremo le molle alla fine del nostro cursore. Quando un utente trascina la maniglia oltre l'intervallo consentito, tornerà indietro verso di essa. Se l'utente rilascia l'handle mentre è fuori dal range, possiamo usare a primavera
animazione per farlo scattare.
Faremo di questo processo una singola funzione che possiamo fornire a pointerX
tubo
metodo. Creando una singola funzione riutilizzabile, possiamo riutilizzare questo pezzo di codice con qualsiasi animazione Popmotion, con intervalli configurabili e punti di forza della molla.
Per prima cosa, applichiamo una molla al limite più a sinistra. Useremo due trasformatori, condizionale
e linearSpring
.
const springRange = (min, max, strength) => condizionale (v => v < min, linearSpring(strength, min) );
condizionale
prende due funzioni, un'asserzione e un trasformatore. L'asserzione riceve il valore fornito e restituisce entrambi vero
o falso
. Se ritorna vero
, alla seconda funzione verrà fornito il valore da trasformare e restituire.
In questo caso, l'affermazione sta dicendo "Se il valore fornito è minore di min
, passare questo valore attraverso il linearSpring
trasformatore. "The linearSpring
è una semplice funzione a molla che, a differenza del fisica
o primavera
animazioni, non ha concetto di tempo. Provalo a forza
e a bersaglio
, e creerà una funzione che "attrae" ogni dato valore verso il bersaglio con la forza definita.
Sostituisci il nostro startDrag
funziona con questo:
const startDrag = () => pointerX (handleX.get ()) .pipe (springRange (0, rangeWidth, 0.1)) .start (handleX);
Stiamo passando il puntatore X
compensare attraverso il nostro springRange
funzione, quindi se trascini la maniglia oltre il lato più a sinistra, noterai che rimorchia.
Applicare lo stesso al lato più a destra è una questione di comporre un secondo condizionale
con il primo utilizzando il solo standalone tubo
funzione:
const springRange = (min, max, strength) => pipe (condizionale (v => v < min, linearSpring(strength, min) ), conditional( v => v> max, linearSpring (strength, max)));
Un altro vantaggio di comporre una funzione come springRange
è che diventa molto testabile. La funzione che restituisce è, come tutti i trasformatori, una funzione pura che prende un singolo valore. Puoi testare questa funzione per vedere se passa attraverso i valori che si trovano all'interno min
e max
inalterato, e se si applica molle a valori che giacciono senza.
Se lasci andare la maniglia mentre si trova al di fuori dell'intervallo, dovrebbe tornare a rientrare nel raggio d'azione. Per questo, dovremo regolare il stopDrag
funzione per sparare a primavera
animazione:
const stopDrag = () => const x = handleX.get (); (X < 0 || x > rangeWidth)? snapHandleToEnd (x): handleX.stop (); ;
Nostro snapHandleToEnd
la funzione si presenta così:
const snapHandleToEnd = (x) => spring (from: x, velocity: handleX.getVelocity (), a: x < 0 ? 0 : rangeWidth, damping: 30, stiffness: 5000 ).start(handleX);
Potete vederlo a
è impostato come 0
o rangeWidth
a seconda di quale lato del cursore si trova attualmente la maniglia. Giocando con smorzamento
e rigidezza
, puoi giocare con una gamma di diverse sensazioni primaverili.
Un bel tocco sullo scrubber di iOS che ho sempre apprezzato è che se si lanciava la maniglia, si rallentava gradualmente anziché fermarsi. Possiamo replicarlo facilmente usando il decadimento
animazione.
Nel stopDrag
, sostituire handleX.stop ()
con momentumScroll (x)
.
Quindi, sulla linea dopo il snapHandleToEnd
funzione, aggiungi una nuova funzione chiamata momentumScroll
:
const momentumScroll = (x) => decay (from: x, velocity: handleX.getVelocity ()). start (handleX);
Ora, se passi la maniglia, arriverà gradualmente. Si animerà anche al di fuori dell'intervallo del cursore. Possiamo fermare questo passando il morsetto
trasformatore al decay.pipe
metodo:
const momentumScroll = (x) => decay (from: x, velocity: handleX.getVelocity ()). pipe (clamp (0, rangeWidth)) .start (handleX);
Usando una combinazione di diverse funzioni di Popmotion, possiamo creare uno scrubber che ha un po 'più di vita e giocosità rispetto al solito.
Usando tubo
, componiamo semplici funzioni pure in comportamenti più complessi lasciando i pezzi compositi testabili e riutilizzabili.
Che ne dici di provare queste sfide: