TimelineMax: Una introducción a la interpolación
Spanish (Español) translation by Steven (you can also view the original English article)



En los viejos tiempos, la interpolación de animación era un término utilizado para describir una secuencia cuadro por cuadro, o lo que a veces se denomina "en medio". Es ese lugar donde un solo movimiento conduce al siguiente para crear un movimiento fluido. Para aquellos que tienen canas, pueden recordar Flash; una aplicación que utilizaba este término cuando se refería a los movimientos del frame. Echemos un vistazo perspicaz a algunos ejemplos y démosle a esta interpolación un buen intento universitario.
Interpolación con TweenMax
Para ambos ejemplos en este tutorial, cargaré TweenMax.min.js, que nos dará acceso a TimelineMax y a todas las otras herramientas centrales increíbles que GSAP tiene para ofrecer. Si piensas en nuestro manual de TimelineMax, hablé de cargar TweenMax.min.js porque es más conveniente, además de que este archivo contiene casi todo lo que necesitaremos (también es lo que recomienda GreenSock el 99% del tiempo).
TweenMax extiende de TweenLite, agregando muchas características útiles (pero no esenciales) como repeat(), repeatDelay(), yoyo(), updateTo() y más. TweenMax se creó para su comodidad y proporciona un único archivo JavaScript que contiene todo lo que necesitarás habitualmente para animar elementos DOM. Básicamente, permite a los autores especificar movimientos individuales, mientras que TimelineMax aceptará métodos encadenados para crear un conjunto más complejo de interpolaciones/secuencias.
Secuencia del cargador
Los 'loaders' son aquellos objetos que se muestran a los usuarios cuando esperan que se cargue un proceso. Nos dan el poder de explorar elementos pequeños y de tamaño micro que pueden tener interacciones complejas o incluso simplistas, además son una maravilla para hacer.
Echa un vistazo rápido a esta demostración de "String of Pearls":
Vamos a dividir la secuencia para comprender mejor cómo funciona todo este asunto de "interpolación".
Para crear esta secuencia requerirá el uso del método staggerTo. Si no recuerdas qué método es, te recomiendo que te detengas en este momento y leas mi tutorial sobre mecánica.
Según la documentación de GreenSock, el método staggerTo():
"Interpola una variedad de objetivos a un conjunto común de valores de destino.
En nuestro caso, esos objetivos múltiples serán cada uno de los círculos dentro de todo el SVG.
- Nuestro primer argumento para
staggerToaceptará el selector que estamos usando para los círculos (en este casocircles). - El segundo argumento será nuestra duración (cuánto durará la animación).
- El tercer argumento es un objeto literal que contiene las propiedades que deseamos interpolar.
- Y el último argumento mantendrá nuestro valor escalonado (la cantidad de tiempo entre el inicio de cada animación).
Esto resultaría en lo siguiente; suponiendo que los círculos contengan tres objetos ...
1 |
timeline.staggerTo(circles, 15, {x:0}, 0.2) |
2 |
|
3 |
// circle 1 starts at time 0
|
4 |
// circle 2 starts at time 0.2
|
5 |
// circle 3 starts at time 0.4
|
Configuración del cargador
Para comenzar bien, necesitaremos definir una nueva línea de tiempo y algunos ajustes para nuestra configuración.
1 |
var loader = new TimelineMax({ repeat: -1, yoyo: true }), |
2 |
circles = $('svg circle'), |
3 |
stagger_options = { |
4 |
opacity: 0, |
5 |
y: -800, |
6 |
ease: Elastic.easeInOut |
7 |
};
|
Para hacer que esta línea de tiempo se repita en la dirección inversa, estoy usando la tecla yoyo y establezco su valor en verdadero. Nuevamente, esto hará que nuestra secuencia se reproduzca en la dirección opuesta una vez que la animación llegue a su cuadro final. La activación de la animación requerirá apuntar a cada círculo dentro del SVG y exactamente por qué necesitaremos una referencia usando el poder de jQuery.
Los documentos señalan varias formas de pasar selectores (siéntete libre de leer más sobre eso aquí). Para este ejemplo, seleccionaré todos los círculos a la vez utilizando la sintaxis de selector típica de jQuery. También es un poco más rápido almacenar nuestra referencia en una variable para reutilizarla más adelante, por lo tanto circles = $('svg circle').
La variable stagger_options es un objeto literal que contiene las propiedades para darle vida a esta animación. Movimos nuestros objetos usando la tecla y porque GSAP CSSPlugin convierte de forma inteligente los valores transform en equivalentes de matriz y, en última instancia, acelera las cosas para nosotros. Hay una lista completa de propiedades de transformación de mano corta que son muy superiores y mucho más fáciles de usar en comparación con las transformaciones CSS típicas:
GSAP equivalente a propiedades CSS
| CSS | GSAP |
|---|---|
translateX() | x |
translateY() | y |
translateZ() | z |
rotate() | rotation |
rotateY() | rotationY |
rotateX() | rotationX |
scaleX() | scaleX |
scaleY() | scaleY |
skewX() | skewX |
skewY() | skewY |
También podemos controlar el suavizado (la sensación de la animación) y pasar varios tipos de movimiento. Para los amantes de lo visual, pueden consultar el visualizador de suavizado de GreenSock para comprender mejor la gran cantidad de suavizados disponibles.



La pieza final de esta creación es adjuntar el método staggerTo a nuestra línea de tiempo e insertar las variables definidas anteriormente y en el orden correcto para este método en particular (elementos, duración, opciones, cantidad de escalonamiento).
1 |
loader.staggerTo(circles, 0.875, stagger_options, 0.025); |
Encadenando una segunda interpolación
Si tienes ganas de crear otra secuencia una vez que se completa la secuencia escalonada, ciertamente podrías encadenar otro método como fromTo así:
1 |
loader.staggerTo(circles, 0.875, stagger_options, 0.025) |
2 |
.fromTo(target: Object, duration: Number, fromVars:{}, toVars: {});
|
Llevarlo más lejos
Intentemos esto con un SVG que he llamado "Polyman". Recientemente escribí un artículo para CSS-Tricks sobre animación de polígonos y decidí usar ese ejemplo similar para otro ejercicio de interpolación aquí. Intentemos usar el método staggerFromTo() y veamos qué tipo de magia podemos conjurar.
El siguiente resultado SVG (el XML) se abrevia un poco en aras de la discusión, pero como verás, nuestro SVG comprende algunas etiquetas; específicamente y <g> y <path>. También ten en cuenta que las rutas que se correlacionan con la cara del hombre se agrupan como secciones para tener un control más preciso sobre el escalonamiento (por ejemplo, orejas, ojos, nariz ...).
1 |
<svg xmlns="http://www.w3.org/2000/svg" viewbox="0 0 800 800" class="polyman"> |
2 |
<g id="ears"> |
3 |
<path fill="#E6BBBE" d="M346.5,344.2l-2.8-3.4l4.1-18.5L346.5,344.2z"></path> |
4 |
<path fill="#EAD9DD" d="M344,332.5l-0.3,8.3l4.1-18.5L344,332.5z"></path> |
5 |
<path fill="#EEDFE3" d="M346,307.1l1.8,15.2l0.4-22.1L346,307.1z"></path> |
6 |
</g>
|
7 |
<!-- and so on -->
|
8 |
</svg>
|
Para la configuración inicial de la línea de tiempo, definiremos nuestras opciones globales utilizando un literal de objeto que retrasará la animación inicial, repetirá la secuencia, luego retrasará la animación nuevamente al repetir y finalmente reproducirá la animación en reversa.
1 |
var tmax_options = { |
2 |
delay: 0.25, |
3 |
repeat: -1, |
4 |
repeatDelay: 0.25, |
5 |
yoyo: true |
6 |
};
|
Forzar valores de transformación
El siguiente es una propiedad bastante nueva e indocumentada que obliga a colocar los valores de transformación en el atributo de transformación SVG (en oposición a un estilo CSS).
1 |
CSSPlugin.useSVGTransformAttr = true; |
Esta propiedad se agregó para facilitar a los desarrolladores la tarea de evitar un error en Safari donde combinar opacidad y transformaciones (como transform: scale(), por ejemplo) produciría un resultado extraño. A partir de 1.16.0 useSVGTransformAttr está establecido en verdadero de forma automática y específicamente dirigido a nuestro amigo Safari, por lo que ya no es necesario que los autores lo definan como lo hice anteriormente.
Dado que el método staggerFromTo acepta argumentos separados para las posiciones from y to, me gusta configurar ambos literales de objeto fuera del método para fines de organización y legibilidad.
1 |
var stagger_opts_from = {
|
2 |
opacity: 0, |
3 |
scale: 0, |
4 |
transformOrigin: 'center center' |
5 |
}; |
6 |
|
7 |
var stagger_opts_to = {
|
8 |
opacity: 1, |
9 |
scale: 1, |
10 |
ease: Elastic.easeInOut, |
11 |
force3D: true |
12 |
}; |
Definimos dos literales de objeto porque necesitamos todas nuestras propiedades from y to definidas para que esta animación haga lo suyo. Si no está claro, lo que haremos será partir de los valores definidos en nuestro stagger_opts_from y terminamos con los valores establecidos en stagger_opts_to.
La tecla force3D obliga a GSAP a aplicar un valor 3D a la transformación del elemento; que significa matrix3d() en lugar de matrix(), o translate3d() en lugar de translate(). Esto normalmente hace que el navegador coloque el elemento seleccionado en su propia capa de compositor, lo que permite actualizaciones más eficientes con animaciones.
De forma predeterminada, force3D está configurado en automático (a partir de 1.15.0), por lo que en realidad no hay necesidad de usarlo (a menos que desees específicamente el comportamiento true en lugar de auto para esa interpolación).
1 |
// layerizes the targets at the start of the tween and
|
2 |
// keeps them that way using a 3D matrix where applicable
|
3 |
// (for 2d and 3d transforms).
|
4 |
force3D:true |
5 |
|
6 |
// layerizes the targets at the beginning of the tween and
|
7 |
// also de-layerizes them (switching back to a 2D matrix or
|
8 |
// transforms) at the end of the tween too. This prevents you
|
9 |
// from creating and hanging onto hundreds of layered elements
|
10 |
// (which can degrade performance) and also makes sure text
|
11 |
// that may have been rasterized during the tween returns to
|
12 |
// being sharp.
|
13 |
force3D:auto |
Puedes establecer el valor global force3D para todas las interpolaciones utilizando la propiedad defaultForce3D proporcionada por CSSPlugin:
1 |
// also accepts 'false' or 'auto'
|
2 |
CSSPlugin.defaultForce3D = true; |
O bien, puedes configurar esto por interpolación:
1 |
// will keep element layered after tween is complete
|
2 |
timeline.to(element, 1, {x:300, force3D:true); |
3 |
|
4 |
// will keep element layered after tween is complete
|
5 |
timeline.to(element, 1, {x:300, force3D:false); |
Ocultar al cargar la ventana
Si tu animación intenta anular las propiedades CSS, debes asegurarte de que tu especificidad particular no colisione con lo que se declara en tu JavaScript; de lo contrario, los valores CSS nativos tendrán prioridad y tu animación no reaccionará como se esperaba.
1 |
/* Required Polyman Styles */
|
2 |
.polyman path { |
3 |
opacity: 0; |
4 |
}
|
El CSS anterior ocultará Polyman en la carga de la ventana inicial, por lo que inicialmente no vemos a nuestro compañero barbudo como lo experimentarías con lo que comúnmente se conoce como FOUT (Flash of Unstyled Text).
Dado que la mayoría de nuestra configuración está definida, finalmente podemos comenzar a configurar nuestra línea de tiempo, apuntar a las rutas SVG, definir un valor escalonado (stagger_val) y finalizar definiendo cuánto durará toda la animación (duration).
1 |
var tl = new TimelineMax(tmax_options), |
2 |
path = $('svg.polyman path'), |
3 |
stagger_val = 0.0125, |
4 |
duration = 1.25; |
¡Así como así y con la delicada ola de nuestra varita mágica y una ligera rociada de polvo de unicornio, pasamos todas las variables necesarias que se definieron como argumentos en el método staggerFromTo!
1 |
tl.staggerFromTo(path, duration, stagger_opts_from, stagger_opts_to, stagger_val, 0); |
¡Y ya está! En una línea simple, el Polyman comienza a respirar y a convertirse en un ser vivo real (bueno, no realmente). Muy cool, ¿no?
Para la próxima vez
En el próximo tutorial de nuestra serie TweenMax, veremos cómo crear un punto de pausa en la línea de tiempo, por lo que la animación se detiene automáticamente cuando llega al punto deseado. El método addPause() es relativamente nuevo y permite colocar una pausa en cualquier parte de una línea de tiempo. Es mucho más preciso que usar un método de devolución de llamada que llama a una función para pausar la función (que es lo que yo tenía que hacer antes de que addPause() existiera). ¡Hasta la próxima feliz interpolación!
¡Un agradecimiento especial a los lectores que siguen este viaje de GreenSock!



