Bienvenido de nuevo a la serie de tutoriales Crear el carrusel perfecto. Estamos haciendo un carrusel encantador y accesible utilizando JavaScript y las capacidades de física, interpolación y seguimiento de entrada de Popmotion..
En la parte 1 de nuestro tutorial, observamos cómo Amazon y Netflix crearon sus carruseles y evaluaron las ventajas y desventajas de sus enfoques. Con nuestros aprendizajes, decidimos una estrategia para nuestro carrusel e implementamos el desplazamiento táctil utilizando la física.
En la parte 2, vamos a implementar el desplazamiento horizontal del mouse. También vamos a ver algunas técnicas comunes de paginación e implementar una. Finalmente, vamos a conectar una barra de progreso que indicará qué tan lejos está el usuario a través del carrusel..
Puedes restaurar tu punto de guardado abriendo este CodePen, que continúa donde lo dejamos.
Es raro que un carrusel de JavaScript respete el desplazamiento horizontal del mouse. Esto es una vergüenza: en computadoras portátiles y ratones que implementan el desplazamiento horizontal basado en el impulso, esta es, con mucho, la forma más rápida de navegar por el carrusel. Es tan malo como forzar a los usuarios táctiles a navegar a través de botones en lugar de deslizar.
Afortunadamente, se puede implementar en solo unas pocas líneas de código. Al final de tu carrusel
función, agregar un nuevo detector de eventos:
container.addEventListener ('wheel', onWheel);
Debajo de tu startTouchScroll
evento, agregar una función de código auxiliar llamada en la rueda
:
función onWheel (e) console.log (e.deltaX)
Ahora, si pasa la rueda de desplazamiento sobre el carrusel y verifica el panel de la consola, verá la distancia de la rueda en la salida del eje x.
Al igual que con el tacto, si el movimiento de la rueda es mayormente vertical, la página debería desplazarse como de costumbre. Si es horizontal, queremos capturar el movimiento de la rueda y aplicarlo al carrusel. Entonces, en en la rueda
, Reemplace la console.log
con:
const angle = calc.angle (x: e.deltaX, y: e.deltaY); if (angleIsVertical (angle)) retorno; e.stopPropagation (); e.preventDefault ();
Este bloque de código detendrá el desplazamiento de página si el desplazamiento es horizontal. Actualizar el desplazamiento x de nuestro deslizador ahora es solo una cuestión de tomar el evento deltaX
propiedad y añadiendo que a nuestro actual sliderX
valor:
const newX = clampXOffset (sliderX.get () + - e.deltaX); sliderX.set (newX);
Estamos reutilizando nuestro anterior. clampXOffset
función para ajustar este cálculo y asegurarse de que el carrusel no se desplace más allá de sus límites medidos.
Cualquier buen tutorial que se ocupe de los eventos de entrada explicará lo importante que es acelerar esos eventos. Esto se debe a que los eventos de desplazamiento, mouse y toque pueden dispararse más rápido que la velocidad de cuadros del dispositivo.
No desea realizar un trabajo innecesario que requiera un uso intensivo de recursos, como renderizar el carrusel dos veces en un cuadro, ya que es un desperdicio de recursos y una forma rápida de crear una interfaz de sensación lenta..
Este tutorial no se ha referido a eso porque los procesadores proporcionados por Popmotion implementan Framesync, un pequeño programador de trabajos sincronizado con cuadros. Esto significa que podrías llamar (v) => sliderRenderer.set ('x', v)
varias veces seguidas, y la representación costosa solo ocurriría una vez, en el siguiente cuadro.
Eso es el desplazamiento terminado. Ahora necesitamos inyectar algo de vida en los botones de navegación hasta ahora no amados.
Ahora, este tutorial trata sobre la interacción, así que siéntase libre de diseñar estos botones como desee. Personalmente, encuentro las flechas de dirección más intuitivas (¡y completamente internacionalizadas de forma predeterminada!).
Hay dos estrategias claras que podríamos tomar al paginar el carrusel: punto por punto o primer objeto oscurecido. Solo hay una estrategia correcta pero, como he visto la otra implementada tan a menudo, pensé que valdría la pena explicar por qué es incorrecto.
Simplemente mida el desplazamiento x del siguiente elemento de la lista y anime el estante esa cantidad. Es un algoritmo muy simple que asumo que se escoge por su simplicidad en lugar de por su facilidad de uso..
El problema es que la mayoría de las pantallas pueden mostrar muchos elementos a la vez, y la gente los escaneará todos antes de intentar navegar..
Se siente lento, si no totalmente frustrante. La única situación en la que esta sería una buena opción es si saber los elementos en su carrusel son del mismo ancho o solo un poco más pequeños que el área visible.
Sin embargo, si estamos viendo varios elementos, es mejor que utilicemos el primer método de elementos ocultos:
Este método simplemente busca la primer objeto oscurecido en la dirección que queramos mover el carrusel, toma su desplazamiento x, y luego se desplaza a ese.
Al hacerlo, obtenemos la cantidad máxima de elementos nuevos que funcionan bajo el supuesto de que el usuario ha visto a todos los presentes en la actualidad..
Debido a que estamos incorporando más elementos, el carrusel requiere menos clics para navegar. Una navegación más rápida aumentará el compromiso y asegurará que sus usuarios vean más de sus productos.
Primero, configuremos nuestros oyentes de eventos para que podamos comenzar a jugar con la paginación.
Primero debemos seleccionar nuestros botones anterior y siguiente. En el parte superior del carrusel
función, añadir:
const nextButton = container.querySelector ('. next'); const prevButton = container.querySelector ('. prev');
Entonces, en el fondo del carrusel
función, añadir los oyentes de eventos:
nextButton.addEventListener ('clic', gotoNext); prevButton.addEventListener ('clic', gotoPrev);
Finalmente, justo encima de su bloque de oyentes de eventos, agregue las funciones reales:
función goto (delta) const gotoNext = () => goto (1); const gotoPrev = () => goto (-1);
ir
Es la función que va a manejar toda la lógica para la paginación. Simplemente toma un número que representa la dirección de viaje que deseamos paginar.. gotoSiguiente
y gotoPrev
simplemente llame a esta función con 1
o -1
, respectivamente.
Un usuario puede desplazarse libremente por este carrusel, y hay norte
artículos dentro de ella, y el carrusel podría ser redimensionado. Entonces, el concepto de una página tradicional no es directamente aplicable aquí. No vamos a contar el número de páginas..
En cambio, cuando el ir
Se llama función, vamos a mirar en la dirección de delta
y encontrar el primer elemento parcialmente oscurecido. Ese será el primer elemento en nuestra próxima "página".
El primer paso es obtener el desplazamiento x actual de nuestro control deslizante, y utilizarlo con el ancho visible total del control deslizante para calcular un desplazamiento "ideal" al que nos gustaría desplazarnos. La compensación ideal es lo que nosotros haría Desplácese hasta si fuéramos ingenuos al contenido del control deslizante. Nos proporciona un buen lugar para comenzar a buscar nuestro primer artículo..
const currentX = sliderX.get (); deja targetX = currentX + (- sliderVisibleWidth * delta);
Podemos utilizar una optimización descarada aquí. Proporcionando nuestra targetX
al clampXOffset
función que hicimos en el tutorial anterior, podemos ver si su salida es diferente a targetX
. Si lo es, significa nuestra targetX
está fuera de nuestros límites desplazables, por lo que no necesitamos averiguar el elemento más cercano. Simplemente nos desplazamos hasta el final.
const clampedX = clampXOffset (targetX); targetX = (targetX === clampedX)? findClosestItemOffset (targetX, delta): clampedX;
Es importante tener en cuenta que el siguiente código funciona en el supuesto de que Todos los artículos en tu carrusel son del mismo tamaño.. Bajo esa suposición, podemos hacer optimizaciones como no tener que medir el tamaño de cada artículo. Si tus articulos son Diferentes tamaños, esto seguirá siendo un buen punto de partida..
Encima de tu ir
función, añadir el findClosestItemOffset
Función referenciada en el último fragmento:
función findClosestItem (targetX, delta)
Primero, necesitamos saber qué tan amplios son nuestros artículos y el espacio entre ellos. los Element.getBoundingClientRect ()
El método puede proporcionar toda la información que necesitamos. por anchura
, simplemente medimos el primer elemento del elemento. Para calcular el espaciado entre elementos, podemos medir el Correcto
desplazamiento del primer elemento y el izquierda
desplaza el segundo, y luego resta el primero del segundo:
const right, width = items [0] .getBoundingClientRect (); const spacing = items [1] .getBoundingClientRect (). left - right;
Ahora, con el targetX
y delta
variables que pasamos a la función, tenemos todos los datos que necesitamos para calcular rápidamente un desplazamiento para desplazarnos hasta.
El cálculo es dividir el absoluto. targetX
valor por el ancho + espacio
. Esto nos dará la cantidad exacta de elementos que podemos colocar dentro de esa distancia.
const totalItems = Math.abs (targetX) / (ancho + espaciado);
Luego, redondee hacia arriba o hacia abajo dependiendo de la dirección de paginación (nuestra delta
). Esto nos dará el número de completar artículos que podemos ajustar.
const totalCompleteItems = delta === 1? Math.floor (totalItems): Math.ceil (totalItems);
Finalmente, multiplica ese número por ancho + espacio
para darnos un color compensado con un elemento completo.
return 0 - totalCompleteItems * (ancho + espaciado);
Ahora que tenemos nuestro targetX
Calculado, podemos animarlo a ello! Para esto, vamos a utilizar el caballo de batalla de la animación web, el entre.
Para los no iniciados, "tween" es la abreviatura de serentre Una interpolación cambia de un valor a otro, durante un período de tiempo establecido. Si has usado transiciones CSS, esto es lo mismo..
Hay una serie de beneficios (y deficiencias) en el uso de JavaScript sobre CSS para interpolaciones. En este caso, porque también estamos animando. sliderX
Con la física y los comentarios de los usuarios, nos será más fácil permanecer en este flujo de trabajo para la interpolación.
También significa que más adelante podemos conectar una barra de progreso y funcionará naturalmente con todas nuestras animaciones, de forma gratuita..
Primero queremos importar entre
de Popmotion:
const calc, css, easing, physics, pointer, transform, tween, value = window.popmotion;
Al final de nuestro ir
función, podemos añadir nuestra interpolación que anima desde corrienteX
a targetX
:
tween (from: currentX, to: targetX, onUpdate: sliderX). start ();
Por defecto, Popmotion establece duración
a 300
milisegundos y facilitar
a easing.easeOut
. Estos han sido seleccionados específicamente para brindar una sensación de respuesta a las animaciones que responden a las sugerencias del usuario, pero siéntase libre de jugar y ver si encuentra algo que se adapte mejor a la sensación de su marca..
Es útil para los usuarios tener alguna indicación sobre dónde están en el carrusel. Para ello, podemos conectar un indicador de progreso..
Su barra de progreso puede ser estilizada de varias maneras. Para este tutorial, hemos creado un div coloreado, 5px alto, que se ejecuta entre los botones anterior y siguiente. Es la forma en que conectamos esto con nuestro código y animamos la barra lo que es importante y es el enfoque de este tutorial.
Aún no has visto el indicador porque originalmente lo diseñamos con transformar: escalaX (0)
. Usamos un escala
transformar para ajustar el ancho de la barra porque, como explicamos en la parte 1, las transformaciones son más eficaces que cambiar propiedades como izquierda
o, en este caso, anchura
.
También nos permite escribir fácilmente un código que establece la escala como un porcentaje: el valor actual de sliderX
Entre MinXOffset
y maxXOffset
.
Empecemos por seleccionar nuestro div.progress-bar
tras nuestro Botón anterior
selector:
const progressBar = container.querySelector ('. progress-bar');
Después de que definamos sliderRenderer
, podemos agregar un renderizador para barra de progreso
:
const progressBarRenderer = css (progressBar);
Ahora vamos a definir una función para actualizar el escalaX
de la barra de progreso.
Usaremos un calc
función llamada getProgressFromValue
. Esto toma un rango, en nuestro caso min
y maxXOffset
, y un tercer número. Devuelve el Progreso, un numero entre 0
y 1
, de ese tercer número dentro del rango dado.
función updateProgressBar (x) const progress = calc.getProgressFromValue (maxXOffset, minXOffset, x); progressBarRenderer.set ('scaleX', progress);
Hemos escrito el rango aquí como maxXOffset, minXOffset
Cuando, intuitivamente, se debe revertir. Esto es porque X
es un valor negativo, y maxXOffset
También es un valor negativo mientras que MinXOffset
es 0
. los 0
es técnicamente el más grande de los dos números, pero el valor más pequeño en realidad representa el desplazamiento máximo. Negativos, eh?
Queremos que el indicador de progreso se actualice en forma sincronizada con sliderX
, Así que vamos a cambiar esta línea:
const sliderX = value (0, (x) => sliderRenderer.set ('x', x));
A esta línea:
const sliderX = value (0, (x) => updateProgressBar (x); sliderRenderer.set ('x', x););
Ahora, cuando sliderX
actualizaciones, igual que la barra de progreso.
Eso es todo por esta entrega! Puede tomar el último código en este CodePen. Hemos introducido con éxito el desplazamiento horizontal de las ruedas, la paginación y una barra de progreso..
¡El carrusel está en muy buena forma hasta ahora! En la entrega final, vamos a ir un paso más allá. Haremos que el carrusel sea completamente accesible para asegurar que cualquiera pueda usarlo.
También vamos a agregar un par de toques encantadores con un tirón accionado por resorte cuando un usuario intenta desplazar el carrusel más allá de sus límites mediante el desplazamiento táctil o la paginación.
Hasta entonces!