Introduzione a Popmotion Scrubber di animazione personalizzato

Nella prima parte della serie introduttiva di Popmotion, abbiamo imparato a usare basato sul tempo animazioni come gemelloi 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 primaveradecadimento, 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 primaveradecadimento per dargli un aspetto più viscerale degli scrubber run-of-the-mill.

Provalo tu stesso:

Iniziare

markup

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.

Funzioni di importazione

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;

Seleziona elementi

Avremo bisogno di tre elementi in questo tutorial. Animeremo il .scatola, trascina e anima il .gestire-hit-zona, e misurare il .gamma.

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

Animazione di fotogrammi chiave

Per la nostra animazione scrubbable, faremo il .scatola sposta da sinistra a destra con i fotogrammi chiave. Tuttavia, potremmo altrettanto scrub a gemellosequenza 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 ();

Trascinando l'asse x

È il momento di usare pointer per trascinare la maniglia del dispositivo di scorrimento. Nel precedente tutorial, abbiamo usato entrambi Xy 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 tubotubo è disponibile su tutte le azioni di Popmotion che abbiamo visto finora, incluso i fotogrammi chiave.

tubo accetta più funzioni. Quando l'azione è inizioed, 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.

Ascoltatori di eventi

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

Muovi la maniglia

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.

scrubbing

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 01, mentre il nostro HANDLEX è impostato con valori di pixel da 0 a rangeWidth.

Possiamo convertire dalla misurazione dei pixel in a 01 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!

The Extra Mile

Confini di primavera

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, condizionalelinearSpring.

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 verofalso. 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 fisicaprimavera 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 minmax 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 0rangeWidth a seconda di quale lato del cursore si trova attualmente la maniglia. Giocando con smorzamentorigidezza, puoi giocare con una gamma di diverse sensazioni primaverili.

Scorrimento del momento

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

Conclusione

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.

Prossimi passi

Che ne dici di provare queste sfide:

  • Rendi il punto di scorrimento dello slancio con un rimbalzo se il manico colpisce entrambe le estremità dello scrubber.
  • Rendi la maniglia animata in qualsiasi punto dello scrubber quando un utente fa clic su un'altra parte della barra della portata.
  • Aggiungi i comandi di riproduzione completa, come un pulsante di riproduzione / pausa. Aggiorna la posizione della maniglia dello scrubber mentre l'animazione procede.