Hacia el “Meta GSAP”: la búsqueda del desplazamiento infinito “perfecto”

No estoy seguro de cómo surgió esto. Pero es una historia. Este artículo trata más sobre cómo un concepto similar, uno que te ayudará a pensar en tus animaciones de una manera diferente. Sucede que este ejemplo en particular presenta desplazamiento infinito, específicamente el desplazamiento infinito “perfecto” para una baraja de cartas sin duplicar ninguna de ellas.

¿Por qué estoy aquí? Bueno, todo esto empezó a partir de un tweet. Un tweet que me hizo pensar en diseños y contenido de desplazamiento lateral.

Tomé ese concepto y lo usé en mi sitio. Y todavía está ahí en acción al momento de escribir este artículo.

Luego comencé a pensar más en las vistas de la galería y los conceptos de desplazamiento lateral. Nos subimos a una transmisión en vivo y sugerimos intentar hacer algo como el antiguo patrón “Cover Flow” de Apple. ¿Recuerdalo?

Lo primero que pensé al hacer esto fue que lo haría para que funcione sin JavaScript, como lo hace en la demostración anterior, de una manera que utilice una “mejora progresiva”. Agarré Greensock y ScrollTrigger y nos fuimos. Salí de ese trabajo bastante decepcionado. Tenía algo pero no podía conseguir que el desplazamiento infinito funcionara como quería. Los botones “Siguiente” y “Anterior” no querían jugar. Puedes verlo aquí y requiere desplazamiento horizontal.

Entonces abrí un nuevo hilo en el foro de Greensock. ¡No sabía que estaba a punto de abrirme a un aprendizaje serio! Solucionamos el problema con los botones. Pero siendo yo, tenía que preguntarme si era posible algo más. ¿Había una forma “limpia” de realizar desplazamiento infinito? Probé algo en la transmisión pero no tuve suerte. Estaba curioso. Pruebe una técnica como la utilizada en este lápiz que creó para la versión ScrollTrigger.

La respuesta inicial fue que es un poco complicado de hacer:

La parte difícil de las cosas infinitas en el desplazamiento es que la barra de desplazamiento es limitada, mientras que el efecto que deseas no lo es. Por lo tanto, debe realizar un bucle en la posición de desplazamiento como en esta demostración (que se encuentra en la sección de demostraciones de ScrollTrigger) o conectarse directamente a los eventos de navegación relacionados con el desplazamiento (como el evento de la rueda) en lugar de usar la posición de desplazamiento real.

Supuse que ese era el caso y estaba feliz de dejarlo “como está”. Pasaron un par de días y Jack dejó una respuesta que me dejó un poco alucinado cuando comenzó a investigarlo. Y ahora, después de mucho repasarlo, estoy aquí para compartir la técnica contigo.

animar cualquier cosa

Una cosa que a menudo se pasa por alto con GSAP es que puedes animar casi cualquier cosa con él. Esto suele deberse a que lo visual es lo que me viene a la mente cuando se piensa en animación: el movimiento físico real de algo. Nuestro primer pensamiento no es llevar ese proceso a un metanivel y animarlo desde un paso atrás.

Pero piense en el trabajo de animación a mayor escala y luego divídalo en capas. Por ejemplo, juega una caricatura. La caricatura es una colección de composiciones. Cada composición es una escena. Y luego tienes el poder de revisar esa colección de composiciones con un control remoto, ya sea en YouTube, usando el control remoto de tu televisor o lo que sea. Hay casi tres niveles en lo que está sucediendo.

Y este es el truco que necesitamos para crear diferentes tipos de bucles infinitos . Este es el concepto principal aquí. Animamos la posición del cabezal de reproducción de una línea de tiempo con una línea de tiempo. Y luego podemos borrar esa línea de tiempo con nuestra posición de desplazamiento.

No te preocupes si eso suena confuso. Vamos a descomponerlo.

Volviéndose “meta”

Comenzamos con un ejemplo. Vamos a crear una interpolación que mueva algunos cuadros de izquierda a derecha. Aquí lo tienes.

Diez cajas que van de izquierda a derecha. Eso es bastante sencillo con Greensock. Aquí usamos fromToy repeatpara mantener la animación. Pero tenemos una brecha al comienzo de cada iteración. También estamos utilizando staggerpara espaciar el movimiento y eso es algo que jugará un papel importante a medida que continuamos.

gsap.fromTo('.box', {  xPercent: 100}, {  xPercent: -200,  stagger: 0.5,  duration: 1,  repeat: -1,  ease: 'none',})

Ahora viene la parte divertida. Pausamos la interpolación y asignamosla a una variable. Luego creamos una interpolación que lo reproduzca. Podemos hacer esto interpolando la totalTimeinterpolación, lo que nos permite obtener o configurar la interpolación del cabezal de reproducción de la interpolación, considerando las repeticiones y los retrasos de repetición.

const SHIFT = gsap.fromTo('.box', {  xPercent: 100}, {  paused: true,  xPercent: -200,  stagger: 0.5,  duration: 1,  repeat: -1,  ease: 'none',})const DURATION = SHIFT.duration()gsap.to(SHIFT, {  totalTime: DURATION,  repeat: -1,  duration: DURATION,  ease: 'none',})

Esta es nuestra primera interpolación “meta”. Se ve exactamente igual pero estamos agregando otro nivel de control. Podemos cambiar cosas en esta capa sin afectar la capa original. Por ejemplo, podríamos cambiar la interpolación easea power4.in. Esto cambia completamente la animación pero sin afectar la animación subyacente. Nos estamos protegiendo un poco con un respaldo.

No sólo eso, podríamos optar por repetir sólo una determinada parte de la línea de tiempo. Podríamos hacer eso con otro fromTo, así:

El código para eso sería algo como esto.

gsap.fromTo(SHIFT, {  totalTime: 2,}, {  totalTime: DURATION - 1,  repeat: -1,  duration: DURATION,  ease: 'none'})

¿Ves a dónde va esto? Cuidado con esa interpolación. Aunque sigue repitiendo, los números cambian en cada repetición. Pero las cajas están en la posición correcta.

Lograr el bucle “perfecto”

Si volvemos a nuestro ejemplo original, hay una brecha notable entre cada repetición.

Aquí viene el truco. La parte que desbloquea todo. Necesitamos construir un bucle perfecto.

Empecemos repitiendo el turno tres veces. Es igual a usar repeat: 3. Observe cómo lo hemos eliminado repeat: -1de la interpolación.

const getShift = () = gsap.fromTo('.box', {  xPercent: 100}, {  xPercent: -200,  stagger: 0.5,  duration: 1,  ease: 'none',})const LOOP = gsap.timeline()  .add(getShift())  .add(getShift())  .add(getShift())

Hemos convertido la interpolación inicial en una función que la devuelve y la agregamos a una nueva línea de tiempo tres veces. Y esto nos da lo siguiente.

DE ACUERDO. Pero todavía hay una brecha. Ahora podemos introducir el positionparámetro para agregar y posicionar esas interpolaciones. Queremos que sea perfecto. Eso significa insertar cada conjunto de interpolaciones antes de que finalice el anterior. Ese es un valor basado en staggery la cantidad de elementos.

const stagger = 0.5 // Used in our shifting tweenconst BOXES = gsap.utils.toArray('.box')const LOOP = gsap.timeline({  repeat: -1})  .add(getShift(), 0)  .add(getShift(), BOXES.length * stagger)  .add(getShift(), BOXES.length * stagger * 2)  

Si actualizamos nuestra línea de tiempo para repetirla y verla (mientras ajustamos staggerpara ver cómo afecta las cosas)…

Notarás que hay una ventana en el medio que crea un bucle "sin fisuras". ¿Recuerdas esas habilidades de antes donde manipulamos el tiempo? Eso es lo que tenemos que hacer aquí: recorrer la ventana de tiempo en la que el bucle es "sin fisuras".

Podríamos intentar intercalar totalTimea través de esa ventana del bucle.

const LOOP = gsap.timeline({  paused: true,  repeat: -1,}).add(getShift(), 0).add(getShift(), BOXES.length * stagger).add(getShift(), BOXES.length * stagger * 2)gsap.fromTo(LOOP, {  totalTime: 4.75,},{  totalTime: '+=5',  duration: 10,  ease: 'none',  repeat: -1,}) 

Aquí, decimos intercalar totalTimedesde 4.75y agregarle la duración de un ciclo. La duración de un ciclo es 5. Y esa es la ventana del medio de la línea de tiempo. Podemos usar el ingenioso GSAP +=para hacer eso, lo que nos da esto:

Tómate un momento para digerir lo que está sucediendo allí. Esta podría ser la parte más complicada de entender. Estamos calculando ventanas de tiempo en nuestra línea de tiempo. Es un poco difícil de visualizar, pero lo intenté.

Esta es una demostración de un reloj cuyas manecillas tardan 12 segundos en dar una vuelta. Se repite infinitamente repeat: -1y luego lo usamos fromTopara animar una ventana de tiempo específica con una duración determinada. Si reduce la ventana de tiempo para decir 2y 6luego cambia la duración a 1, las manecillas pasarán de las 2 en punto a las 6 en punto una y otra vez. Pero nunca cambiamos la animación subyacente.

Intente configurar los valores para ver cómo afecta las cosas.

En este punto, es una buena idea elaborar una fórmula para la posición de nuestra ventana. También podríamos usar una variable para el tiempo que tarda cada cuadro en realizar la transición.

const DURATION = 1const CYCLE_DURATION = BOXES.length * STAGGERconst START_TIME = CYCLE_DURATION + (DURATION * 0.5)const END_TIME = START_TIME + CYCLE_DURATION

En lugar de utilizar tres líneas de tiempo apiladas, podríamos recorrer nuestros elementos tres veces, obteniendo el beneficio de no tener que calcular las posiciones. Sin embargo, visualizar esto como tres líneas de tiempo apiladas es una buena manera de asimilar el concepto y una buena manera de ayudar a comprender la idea principal.

Cambiemos nuestra implementación para crear una gran línea de tiempo desde el principio.

const STAGGER = 0.5const BOXES = gsap.utils.toArray('.box')const LOOP = gsap.timeline({  paused: true,  repeat: -1,})const SHIFTS = [...BOXES, ...BOXES, ...BOXES]SHIFTS.forEach((BOX, index) = {  LOOP.fromTo(BOX, {    xPercent: 100  }, {    xPercent: -200,    duration: 1,    ease: 'none',  }, index * STAGGER)})

Esto es más fácil de armar y nos da la misma ventana. Pero no necesitamos pensar en matemáticas. Ahora recorremos tres conjuntos de cuadros y posicionamos cada animación de acuerdo con el escalonamiento.

¿Cómo se vería eso si ajustamos el escalonamiento? Aplastará las cajas más juntas.

Pero se rompió la ventana porque ahora está totalTimeafuera. Necesitamos recalcular la ventana. Ahora es un buen momento para introducir la fórmula que calculamos anteriormente.

const DURATION = 1const CYCLE_DURATION = STAGGER * BOXES.lengthconst START_TIME = CYCLE_DURATION + (DURATION * 0.5)const END_TIME = START_TIME + CYCLE_DURATIONgsap.fromTo(LOOP, {  totalTime: START_TIME,},{  totalTime: END_TIME,  duration: 10,  ease: 'none',  repeat: -1,})

¡Fijado!

Incluso podríamos introducir una “compensación” si quisiéramos cambiar la posición inicial.

const STAGGER = 0.5const OFFSET = 5 * STAGGERconst START_TIME = (CYCLE_DURATION + (STAGGER * 0.5)) + OFFSET 

Ahora nuestra ventana comienza desde una posición diferente.

Pero aún así, esto no es genial ya que nos da estas incómodas pilas en cada extremo. Para eliminar ese efecto, debemos pensar en una ventana “física” para nuestras cajas. O piense en cómo entran y salen de la escena.

La usaremos document.bodycomo ventana para nuestro ejemplo. Actualicemos las interpolaciones de cuadros para que sean líneas de tiempo individuales donde los cuadros aumentan al entrar y disminuyen al salir. Podemos usar yoyoy repeat: 1para lograr entrar y salir.

SHIFTS.forEach((BOX, index) = {  const BOX_TL = gsap    .timeline()    .fromTo(      BOX,      {        xPercent: 100,      },      {        xPercent: -200,        duration: 1,        ease: 'none',      }, 0    )    .fromTo(      BOX,      {        scale: 0,      },      {        scale: 1,        repeat: 1,        yoyo: true,        ease: 'none',        duration: 0.5,      },      0    )  LOOP.add(BOX_TL, index * STAGGER)})  

¿Por qué utilizamos una duración de línea de tiempo de 1? Hace que las cosas sean más fáciles de seguir. Sabemos que el momento es 0.5cuando la caja está en el punto medio. Vale la pena señalar que la flexibilización no tendrá el efecto que normalmente pensamos aquí. De hecho, la flexibilización influirá en cómo se posicionan las cajas. Por ejemplo, un ease-inagruparía las cajas a la derecha antes de cruzarlas.

El código anterior nos da esto.

Casi. Pero nuestras cajas desaparecen por un tiempo a la mitad. Para solucionar este problema, introduzcamos la immediateRenderpropiedad. Actúa como animation-fill-mode: noneen CSS. Le estamos diciendo a GSAP que no queremos retener ni pregrabar ningún estilo que se esté configurando en un cuadro.

SHIFTS.forEach((BOX, index) = {  const BOX_TL = gsap    .timeline()    .fromTo(      BOX,      {        xPercent: 100,      },      {        xPercent: -200,        duration: 1,        ease: 'none',        immediateRender: false,      }, 0    )    .fromTo(      BOX,      {        scale: 0,      },      {        scale: 1,        repeat: 1,        zIndex: BOXES.length + 1,        yoyo: true,        ease: 'none',        duration: 0.5,        immediateRender: false,      },      0    )  LOOP.add(BOX_TL, index * STAGGER)})  

¡Ese pequeño cambio nos arregla las cosas! Observe cómo también hemos incluido z-index: BOXES.length. Eso debería protegernos contra cualquier z-indexproblema.

¡Ahí lo tenemos! Nuestro primer bucle infinito sin interrupciones. Sin elementos duplicados y continuación perfecta. ¡Estamos doblando el tiempo! ¡Date una palmadita en la espalda si has llegado hasta aquí!

Si queremos ver más cuadros a la vez, podemos jugar con el tiempo, el escalonamiento y la facilidad. Aquí tenemos un STAGGERde 0.2y también lo hemos introducido opacityen la mezcla.

La parte clave aquí es que podemos aprovecharlo repeatDelaypara que la opacitytransición sea más rápida que la escala. Se desvanece en más de 0,25 segundos. Espere 0,5 segundos. Vuelve a desvanecerse en 0,25 segundos.

.fromTo(  BOX, {    opacity: 0,  }, {    opacity: 1,    duration: 0.25,    repeat: 1,    repeatDelay: 0.5,    immediateRender: false,    ease: 'none',    yoyo: true,  }, 0)   

¡Fresco! Podríamos hacer lo que queramos con esas transiciones de entrada y salida. Lo principal aquí es que tenemos nuestra ventana de tiempo que nos da el bucle infinito.

Conectando esto para desplazarse

Ahora que tenemos un bucle continuo, adjuntémoslo para desplazarnos. Para esto podemos usar ScrollTrigger de GSAP. Esto requiere una interpolación adicional para borrar nuestra ventana de bucle. Observe también cómo hemos configurado el bucle pausedahora.

const LOOP_HEAD = gsap.fromTo(LOOP, {  totalTime: START_TIME,},{  totalTime: END_TIME,  duration: 10,  ease: 'none',  repeat: -1,  paused: true,})const SCRUB = gsap.to(LOOP_HEAD, {  totalTime: 0,  paused: true,  duration: 1,  ease: 'none',}) 

El truco aquí consiste en ScrollTriggerlimpiar el encabezado de reproducción del bucle actualizando el totalTimearchivo of SCRUB. Hay varias maneras en que podríamos configurar este pergamino. Podríamos tenerlo horizontal o atado a un contenedor. Pero lo que vamos a hacer es envolver nuestras cajas en un .boxeselemento y fijarlo en la ventana gráfica. (Esto fija su posición en la ventana gráfica). También nos quedaremos con el desplazamiento vertical. Consulte la demostración para ver el estilo .boxesque configura las cosas al tamaño de la ventana gráfica.

import ScrollTrigger from 'https://cdn.skypack.dev/gsap/ScrollTrigger'gsap.registerPlugin(ScrollTrigger)ScrollTrigger.create({  start: 0,  end: '+=2000',  horizontal: false,  pin: '.boxes',  onUpdate: self = {    SCRUB.vars.totalTime = LOOP_HEAD.duration() * self.progress    SCRUB.invalidate().restart()  }})

Lo importante está en el interior onUpdate. Ahí es donde configuramos la totalTimeinterpolación según el progreso del desplazamiento. La invalidatellamada borra cualquier posición registrada internamente para la limpieza. Luego restartestablece la posición a la nueva totalTimeque configuramos.

¡Pruébalo! Podemos avanzar y retroceder en la línea de tiempo y actualizar la posición.

¿Cuan genial es eso? Podemos desplazarnos para borrar una línea de tiempo que borra una línea de tiempo que es una ventana de una línea de tiempo. Digieran eso por un segundo porque eso es lo que está pasando aquí.

Viaje en el tiempo para desplazamiento infinito

Hasta ahora hemos estado manipulando el tiempo. ¡Ahora vamos a viajar en el tiempo!

Para hacer esto, usaremos otras utilidades GSAP y ya no borraremos el totalTimearchivo LOOP_HEAD. En lugar de eso, lo actualizaremos mediante proxy. Este es otro gran ejemplo de cómo convertirnos en “meta” GSAP.

Comencemos con un objeto proxy que marca la posición del cursor de reproducción.

const PLAYHEAD = { position: 0 }

Ahora podemos actualizar nuestro SCRUBpara actualizar position. Al mismo tiempo, podemos utilizar la wraputilidad de GSAP, que ajusta el positionvalor a la LOOP_HEADduración. Por ejemplo, si la duración es 10y proporcionamos el valor 11, lo recuperaremos 1.

const POSITION_WRAP = gsap.utils.wrap(0, LOOP_HEAD.duration())const SCRUB = gsap.to(PLAYHEAD, {  position: 0,  onUpdate: () = {    LOOP_HEAD.totalTime(POSITION_WRAP(PLAYHEAD.position))  },  paused: true,  duration: 1,  ease: 'none',})

Por último, pero no menos importante, debemos revisar ScrollTrigger para que actualice la variable correcta en el archivo SCRUB. Eso es position, en lugar de totalTime.

ScrollTrigger.create({  start: 0,  end: '+=2000',  horizontal: false,  pin: '.boxes',  onUpdate: self = {    SCRUB.vars.position = LOOP_HEAD.duration() * self.progress    SCRUB.invalidate().restart()  }})

En este punto, hemos cambiado a un proxy y no veremos ningún cambio.

Queremos un bucle infinito cuando nos desplazamos. Nuestro primer pensamiento podría ser desplazarnos hasta el inicio cuando completemos el progreso del desplazamiento. Y haría exactamente eso, desplazarse hacia atrás. Aunque eso es lo que queremos hacer, no queremos que el cabezal de reproducción retroceda. Aquí es donde totalTimeentra en juego. ¿Recuerdas? Obtiene o establece la posición del cabezal de reproducción según lo totalDurationque incluye repeticiones y retrasos de repetición.

Por ejemplo, supongamos que la duración del inicio del bucle fue 5y llegamos allí, no volveremos a 0. En su lugar, seguiremos desplazando el inicio del bucle a 10. Si continuamos, llegará a 15, y así sucesivamente. Mientras tanto, realizaremos un seguimiento de una iterationvariable porque eso nos dice en qué parte del matorral nos encontramos. También nos aseguraremos de actualizar solo iterationcuando alcancemos los umbrales de progreso.

Empecemos con una iterationvariable:

let iteration = 0

Ahora actualicemos nuestra implementación de ScrollTrigger:

const TRIGGER = ScrollTrigger.create({  start: 0,  end: '+=2000',  horizontal: false,  pin: '.boxes',  onUpdate: self = {    const SCROLL = self.scroll()    if (SCROLL  self.end - 1) {      // Go forwards in time      WRAP(1, 1)    } else if (SCROLL  1  self.direction ; 0) {      // Go backwards in time      WRAP(-1, self.end - 1)    } else {      SCRUB.vars.position = (iteration + self.progress) * LOOP_HEAD.duration()      SCRUB.invalidate().restart()     }  }}) 

Observe cómo ahora estamos teniendo iterationen cuenta el positioncálculo. Recuerda que esto se envuelve con el estropajo. También detectamos cuándo alcanzamos los límites de nuestro desplazamiento, y ese es el punto donde WRAP. Esta función establece el iterationvalor apropiado y establece la nueva posición de desplazamiento.

const WRAP = (iterationDelta, scrollTo) = {  iteration += iterationDelta  TRIGGER.scroll(scrollTo)  TRIGGER.update()}

¡Tenemos desplazamiento infinito! Si tienes uno de esos elegantes ratones con rueda de desplazamiento que puedes soltar, ¡pruébalo! ¡Es divertido!

Aquí hay una demostración que muestra el actual iterationy progress:

Ajuste de desplazamiento

Estaban allí. Pero siempre hay "algo bueno" cuando se trabaja en una característica como esta. Comencemos con el ajuste de desplazamiento. GSAP hace que esto sea fácil, ya que podemos usarlo gsap.utils.snapsin ninguna otra dependencia. Eso se encarga de llegar a un momento en el que proporcionamos los puntos. Declaramos el paso entre 0y 1y tenemos 10cajas en nuestro ejemplo. Eso significa que un chasquido 0.1funcionaría para nosotros.

const SNAP = gsap.utils.snap(1 / BOXES.length)

Y eso devuelve una función que podemos usar para ajustar nuestro positionvalor.

Sólo queremos tomar la foto una vez que el desplazamiento haya terminado. Para eso, podemos usar un detector de eventos en ScrollTrigger. Cuando termine el desplazamiento, nos desplazaremos hasta un determinado position.

ScrollTrigger.addEventListener('scrollEnd', () = {  scrollToPosition(SCRUB.vars.position)})

Y aquí está scrollToPosition:

const scrollToPosition = position = {  const SNAP_POS = SNAP(position)  const PROGRESS =    (SNAP_POS - LOOP_HEAD.duration() * iteration) / LOOP_HEAD.duration()  const SCROLL = progressToScroll(PROGRESS)  TRIGGER.scroll(SCROLL)}

¿Qué estamos haciendo aquí?

  1. Calcular el punto en el tiempo al que ajustarse
  2. Calculando el progreso actual. Digamos que LOOP_HEAD.duration()es 1y hemos elegido 2.5. Eso nos da un progreso que 0.5resulta en un iterationof 2, donde 2.5 - 1 * 2 / 1 === 0.5. Calculamos el progreso para que siempre esté entre 1y 0.
  3. Calculando el destino del desplazamiento. Esta es una fracción de la distancia que puede cubrir nuestro ScrollTrigger. En nuestro ejemplo, hemos establecido una distancia de 2000y queremos una fracción de esa. Creamos una nueva función progressToScrollpara calcularlo.
const progressToScroll = progress =  gsap.utils.clamp(1, TRIGGER.end - 1, gsap.utils.wrap(0, 1, progress) * TRIGGER.end)

Esta función toma el valor de progreso y lo asigna a la distancia de desplazamiento más grande. Pero usamos una abrazadera para asegurarnos de que el valor nunca pueda ser 0o 2000. Esto es importante. Nos estamos protegiendo contra el ajuste a estos valores, ya que nos pondría en un bucle infinito.

Hay un poco que asimilar allí. Consulte esta demostración que muestra los valores actualizados en cada instantánea.

¿Por qué las cosas son mucho más ágiles? Se ha modificado la duración y la facilidad del fregado. Una duración menor y una facilidad más contundente nos dan la oportunidad.

const SCRUB = gsap.to(PLAYHEAD, {  position: 0,  onUpdate: () = {    LOOP_HEAD.totalTime(POSITION_WRAP(PLAYHEAD.position))  },  paused: true,  duration: 0.25,  ease: 'power3',})

Pero si jugaste con esa demostración, notarás que hay un problema. A veces, cuando nos envolvemos dentro del complemento, el cabezal de reproducción salta. Necesitamos tener esto en cuenta asegurándonos de ajustar cuando rompamos, pero solo cuando sea necesario.

const scrollToPosition = position = {  const SNAP_POS = SNAP(position)  const PROGRESS =    (SNAP_POS - LOOP_HEAD.duration() * iteration) / LOOP_HEAD.duration()  const SCROLL = progressToScroll(PROGRESS)  if (PROGRESS = 1 || PROGRESS  0) return WRAP(Math.floor(PROGRESS), SCROLL)  TRIGGER.scroll(SCROLL)}

¡Y ahora tenemos desplazamiento infinito con ajuste!

¿Qué sigue?

Hemos completado el trabajo preliminar para un desplazamiento infinito sólido. Podemos aprovechar eso para agregar cosas, como controles o funcionalidad de teclado. Por ejemplo, esta podría ser una forma de conectar los botones y controles del teclado "Siguiente" y "Anterior". Todo lo que tenemos que hacer es manipular el tiempo, ¿verdad?

const NEXT = () = scrollToPosition(SCRUB.vars.position - (1 / BOXES.length))const PREV = () = scrollToPosition(SCRUB.vars.position + (1 / BOXES.length))// Left and Right arrow plus A and Ddocument.addEventListener('keydown', event = {  if (event.keyCode === 37 || event.keyCode === 65) NEXT()  if (event.keyCode === 39 || event.keyCode === 68) PREV()})document.querySelector('.next').addEventListener('click', NEXT)document.querySelector('.prev').addEventListener('click', PREV)

Eso podría darnos algo como esto.

Podemos aprovechar nuestra scrollToPositionfunción y aumentar el valor según necesitemos.

¡Eso es todo!

¿Mira eso? ¡GSAP puede animar más que elementos! Aquí, doblamos y manipulamos el tiempo para crear un control deslizante infinito casi perfecto. Sin elementos duplicados, sin desorden y con buena flexibilidad.

Recapitulemos lo que cubrimos:

  • Podemos animar una animación.
  • Podemos pensar en el tiempo como una herramienta de posicionamiento cuando manipulamos el tiempo.
  • Cómo utilizar ScrollTrigger para borrar una animación a través de proxy.
  • Cómo utilizar algunas de las increíbles utilidades de GSAP para manejar la lógica por nosotros.

¡Ahora puedes manipular el tiempo!

Ese concepto de convertirse en GSAP “meta” abre una variedad de posibilidades. ¿Qué más podrías animar? ¿Audio? ¿Video? En cuanto a la demostración de "Cover Flow", ¡aquí es donde fue!

Deja un comentario

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *

Subir