Bentornati alla serie di tutorial Create the Perfect Carousel. Stiamo realizzando un carosello accessibile e piacevole utilizzando JavaScript, la fisica di Popmotion, le funzionalità di tracciamento tra input e input.
Nella parte 1 del nostro tutorial, abbiamo dato un'occhiata a come Amazon e Netflix hanno creato i loro caroselli e valutato i pro e i contro dei loro approcci. Con i nostri apprendimenti, abbiamo deciso una strategia per il nostro carosello e abbiamo implementato il touch scroll utilizzando la fisica.
Nella parte 2, implementeremo la scroll orizzontale del mouse. Vedremo anche alcune tecniche di impaginazione comuni e ne implementeremo una. Infine, collegheremo una barra di avanzamento che indicherà quanto è lontano il carosello dell'utente.
Puoi ripristinare il tuo punto di salvataggio aprendo questo CodePen, che riprende da dove avevamo interrotto.
È raro che un carosello JavaScript rispetti lo scorrimento orizzontale del mouse. Questo è un peccato: su laptop e mouse che implementano lo scorrimento orizzontale basato sul momento, questo è di gran lunga il modo più rapido per navigare nel carosello. È così difficile come costringere gli utenti touch a navigare attraverso i pulsanti piuttosto che scorrere.
Fortunatamente, può essere implementato in poche righe di codice. Alla fine del tuo giostra
funzione, aggiungi un nuovo listener di eventi:
container.addEventListener ('wheel', onWheel);
Sotto il tuo startTouchScroll
evento, aggiungere una funzione di stub chiamata onWheel
:
funzione onWheel (e) console.log (e.deltaX)
Ora, se esegui la rotella di scorrimento sul carosello e controlli il pannello della console, vedrai la distanza della ruota sull'uscita dell'asse x.
Come nel caso del tocco, se il movimento della ruota è prevalentemente verticale, la pagina dovrebbe scorrere come al solito. Se è orizzontale, vogliamo catturare il movimento della ruota e applicarlo alla giostra. Quindi, in onWheel
, sostituisci il console.log
con:
const angle = calc.angle (x: e.deltaX, y: e.deltaY); se (angleIsVertical (angle)) restituisce; e.stopPropagation (); e.preventDefault ();
Questo blocco di codice interromperà lo scorrimento della pagina se lo scorrimento è orizzontale. L'aggiornamento dell'offset x del nostro slider è ora solo una questione di prendere quella dell'evento deltaX
proprietà e aggiungendolo alla nostra corrente sliderX
valore:
const newX = clampXOffset (sliderX.get () + - e.deltaX); sliderX.set (newX);
Stiamo riutilizzando il nostro precedente clampXOffset
funzione per avvolgere questo calcolo e assicurarsi che la giostra non scorra oltre i suoi limiti misurati.
Qualsiasi buon tutorial che tratti gli eventi di input spiegherà quanto sia importante limitare questi eventi. Questo perché gli eventi di scorrimento, mouse e tocco possono attivarsi più velocemente rispetto alla frequenza fotogrammi del dispositivo.
Non si desidera eseguire un lavoro non necessario a uso intensivo delle risorse come il rendering del carosello due volte in un frame, in quanto è uno spreco di risorse e un modo rapido per creare un'interfaccia sensata..
Questo tutorial non ha toccato questo perché i renderer forniti da Popmotion implementano Framesync, un piccolo schedulatore di lavoro sincronizzato con frame. Questo significa che puoi chiamare (v) => sliderRenderer.set ('x', v)
più volte di seguito e il rendering costoso si verifica solo una volta, nel fotogramma successivo.
Lo scrolling è finito. Ora abbiamo bisogno di iniettare un po 'di vita nei pulsanti di navigazione finora non amati.
Ora, questo tutorial riguarda l'interazione, quindi sentiti libero di progettare questi pulsanti come desideri. Personalmente, trovo le frecce di direzione più intuitive (e completamente internazionalizzate di default!).
Ci sono due strategie chiare che possiamo prendere quando impaginiamo la giostra: voce per voce o primo oggetto oscurato. C'è solo una strategia corretta ma, poiché ho visto l'altro implementato così spesso, ho pensato che valesse la pena di spiegarlo perché non è corretto.
Basta misurare l'offset x dell'elemento successivo nell'elenco e animare lo scaffale di tale importo. È un algoritmo molto semplice che presumo sia scelto per la sua semplicità piuttosto che per la sua facilità d'uso.
Il problema è che la maggior parte delle schermate sarà in grado di mostrare molti oggetti alla volta e le persone li scannerizzeranno tutti prima di provare a navigare.
Sembra lento, se non addirittura frustrante. L'unica situazione in cui questa sarebbe una buona scelta è se tu conoscere gli oggetti nel tuo carosello hanno la stessa larghezza o solo leggermente più piccoli dell'area visibile.
Tuttavia, se stiamo esaminando più elementi, è meglio utilizzare il primo metodo di elemento oscurato:
Questo metodo cerca semplicemente il primo oggetto oscurato nella direzione in cui vogliamo spostare la giostra, prende il suo x offset, e poi scorre fino a quello.
Nel fare ciò, inseriamo il numero massimo di nuovi articoli lavorando sul presupposto che l'utente abbia visto tutti quelli attualmente presenti.
Poiché stiamo introducendo più articoli, la giostra richiede meno clic per spostarsi. Una navigazione più rapida aumenterà il coinvolgimento e consentirà agli utenti di vedere più prodotti.
Per prima cosa, impostiamo i nostri ascoltatori di eventi in modo che possiamo iniziare a giocare con l'impaginazione.
Per prima cosa dobbiamo selezionare i nostri pulsanti precedente e successivo. Al superiore del giostra
funzione, aggiungere:
const nextButton = container.querySelector ('. next'); const prevButton = container.querySelector ('. prev');
Quindi, al parte inferiore del giostra
funzione, aggiungi i listener di eventi:
nextButton.addEventListener ('click', gotoNext); prevButton.addEventListener ('click', gotoPrev);
Infine, appena sopra il tuo blocco di ascoltatori di eventi, aggiungi le funzioni effettive:
function goto (delta) const gotoNext = () => goto (1); const gotoPrev = () => goto (-1);
vai a
è la funzione che gestirà tutta la logica per l'impaginazione. Prende semplicemente un numero che rappresenta la direzione di viaggio che desideriamo impaginare. gotoNext
e gotoPrev
chiama semplicemente questa funzione con 1
o -1
, rispettivamente.
Un utente può scorrere liberamente questo carosello, e ci sono n
elementi al suo interno e il carosello potrebbe essere ridimensionato. Quindi il concetto di una pagina tradizionale non è direttamente applicabile qui. Non conteremo il numero di pagine.
Invece, quando il vai a
la funzione è chiamata, stiamo andando a guardare nella direzione di delta
e trova il primo oggetto parzialmente oscurato. Questo diventerà il primo oggetto della nostra prossima "pagina".
Il primo passo è ottenere l'attuale x offset del nostro slider e usarlo con la piena larghezza visibile del cursore per calcolare un offset "ideale" al quale vorremmo scorrere. L'offset ideale è ciò che noi voluto scorrere fino se fossimo ingenui con il contenuto del cursore. Fornisce un bel posto per noi per iniziare a cercare il nostro primo oggetto.
const currentX = sliderX.get (); let targetX = currentX + (- sliderVisibleWidth * delta);
Possiamo usare un'ottimizzazione sfacciata qui. Fornendo il nostro TargetX
al clampXOffset
funzione che abbiamo fatto nel tutorial precedente, possiamo vedere se il suo output è diverso da TargetX
. Se lo è, significa il nostro TargetX
è al di fuori dei nostri confini scorrevoli, quindi non abbiamo bisogno di capire l'oggetto più vicino. Scorriamo fino alla fine.
const clampedX = clampXOffset (targetX); targetX = (targetX === clampedX)? findClosestItemOffset (targetX, delta): clampedX;
È importante notare che il seguente codice funziona sul presupposto che tutti gli articoli nel tuo carosello hanno le stesse dimensioni. Sotto questa ipotesi, possiamo fare ottimizzazioni come non dover misurare le dimensioni di ogni oggetto. Se i tuoi articoli siamo diverse dimensioni, questo sarà comunque un buon punto di partenza.
Sopra il tuo vai a
funzione, aggiungi il findClosestItemOffset
funzione a cui si fa riferimento nell'ultimo snippet:
function findClosestItem (targetX, delta)
Per prima cosa, dobbiamo sapere quanto sono ampi i nostri articoli e la loro distanza. Il Element.getBoundingClientRect ()
il metodo può fornire tutte le informazioni di cui abbiamo bisogno. Per larghezza
, misuriamo semplicemente il primo elemento elemento. Per calcolare la spaziatura tra gli elementi, possiamo misurare il destra
offset del primo oggetto e del sinistra
offset del secondo, e quindi sottrarre il primo da quest'ultimo:
const right, width = items [0] .getBoundingClientRect (); const spacing = items [1] .getBoundingClientRect (). left - right;
Ora, con il TargetX
e delta
le variabili che abbiamo passato alla funzione, abbiamo tutti i dati di cui abbiamo bisogno per calcolare velocemente un offset su cui scorrere.
Il calcolo è dividere l'assoluto TargetX
valore dal larghezza + spaziatura
. Questo ci darà il numero esatto di oggetti che possiamo inserire all'interno di quella distanza.
const totalItems = Math.abs (targetX) / (larghezza + spaziatura);
Quindi, arrotondare in su o in giù a seconda della direzione di impaginazione (il nostro delta
). Questo ci darà il numero di completare articoli che possiamo adattare.
const totalCompleteItems = delta === 1? Math.floor (totalItems): Math.ceil (totalItems);
Infine, moltiplica quel numero per larghezza + spaziatura
per darci una compensazione a filo con un oggetto completo.
return 0 - totalCompleteItems * (width + spacing);
Ora che abbiamo il nostro TargetX
calcolato, possiamo animarci! Per questo, useremo il cavallo di battaglia dell'animazione web, il gemello.
Per chi non lo sapesse, "tween" è l'abbreviazione di esseregemello. Un'interpolazione cambia da un valore a un altro, per un periodo di tempo impostato. Se hai utilizzato le transizioni CSS, questa è la stessa cosa.
Ci sono una serie di vantaggi (e mancanze!) Nell'uso di JavaScript su CSS per le interpolazioni. In questo caso, perché stiamo anche animando sliderX
con la fisica e l'input dell'utente, sarà più facile per noi rimanere in questo flusso di lavoro per l'interpolazione.
Significa anche che in seguito possiamo collegare una barra di avanzamento e funzionerà naturalmente con tutte le nostre animazioni, gratuitamente.
Per prima cosa vogliamo importare gemello
da Popmotion:
const calc, css, easing, physics, pointer, transform, tween, value = window.popmotion;
Alla fine del nostro vai a
funzione, possiamo aggiungere la nostra interpolazione da cui si anima CurrentX
a TargetX
:
interpolazione (da: currentX, a: targetX, onUpdate: sliderX). start ();
Di default, Popmotion imposta durata
a 300
millisecondi e alleviare
a easing.easeOut
. Questi sono stati scelti appositamente per dare un tocco reattivo alle animazioni che rispondono all'input dell'utente, ma sentitevi liberi di giocare e vedere se vi viene in mente qualcosa che si adatta meglio al tatto del vostro marchio.
È utile che gli utenti abbiano qualche indicazione su dove si trovano nella giostra. Per questo, possiamo collegare un indicatore di progresso.
La barra di avanzamento potrebbe essere abbinata in diversi modi. Per questo tutorial, abbiamo realizzato un div colorato, alto 5px, che scorre tra i pulsanti precedente e successivo. È il modo in cui lo colleghiamo al nostro codice e animiamo la barra che è importante ed è il fulcro di questo tutorial.
Non hai ancora visto l'indicatore perché inizialmente lo abbiamo disegnato trasformare: scaleX (0)
. Noi usiamo a scala
trasforma per regolare la larghezza della barra perché, come abbiamo spiegato nella parte 1, le trasformazioni sono più performanti rispetto alla modifica delle proprietà come sinistra
o, in questo caso, larghezza
.
Ci consente anche di scrivere facilmente il codice che imposta la scala come a percentuale: il valore corrente di sliderX
fra minXOffset
e maxXOffset
.
Iniziamo selezionando il nostro div.progress bar
dopo il nostro previousButton
selettore:
const progressBar = container.querySelector ('. progress-bar');
Dopo aver definito sliderRenderer
, possiamo aggiungere un renderer per barra di avanzamento
:
const progressBarRenderer = css (progressBar);
Ora definiamo una funzione per aggiornare il scaleX
della barra di avanzamento.
Useremo a calc
funzione chiamata getProgressFromValue
. Questo richiede un intervallo, nel nostro caso min
e maxXOffset
, e un terzo numero. Restituisce il progresso, un numero tra 0
e 1
, di quel terzo numero nell'intervallo specificato.
function updateProgressBar (x) const progress = calc.getProgressFromValue (maxXOffset, minXOffset, x); progressBarRenderer.set ('scaleX', progress);
Abbiamo scritto la gamma qui come maxXOffset, minXOffset
quando, intuitivamente, dovrebbe essere invertito. Questo è perché X
è un valore negativo, e maxXOffset
è anche un valore negativo mentre minXOffset
è 0
. Il 0
è tecnicamente il più grande dei due numeri, ma il valore più piccolo rappresenta in realtà l'offset massimo. Negativi, eh?
Vogliamo che l'indicatore di avanzamento si aggiorni in pochi istanti con sliderX
, quindi cambiamo questa linea:
const sliderX = value (0, (x) => sliderRenderer.set ('x', x));
A questa linea:
const sliderX = value (0, (x) => updateProgressBar (x); sliderRenderer.set ('x', x););
Ora, ogni volta sliderX
aggiornamenti, così sarà la barra di avanzamento.
Questo è tutto per questa puntata! Puoi prendere l'ultimo codice su questo CodePen. Abbiamo introdotto con successo lo scorrimento orizzontale delle ruote, l'impaginazione e una barra di avanzamento.
La giostra è abbastanza buona finora! Nell'ultima puntata, faremo un ulteriore passo in avanti. Renderemo il carosello completamente accessibile da tastiera per garantire che chiunque possa usarlo.
Aggiungiamo anche un paio di deliziosi tocchi usando un rimorchiatore a molla quando un utente prova a far scorrere la giostra oltre i suoi confini usando il touch scroll o l'impaginazione.
Ci vediamo!