Controlar animaciones y transiciones CSS con JavaScript | Programar Plus

La siguiente es una publicación invitada de Zach Saucier. Zach me escribió diciéndome que, como frecuentador de foros de codificación como Stack Overflow, ve que surgen preguntas todo el tiempo sobre el control de animaciones CSS con JavaScript, y lo demostró con un montón de enlaces. He tenido esto en mi lista para escribir durante demasiado tiempo, así que estaba feliz de dejar que Zach profundizara y escribiera este tutorial completo.

Los diseñadores web a veces creen que animar en CSS es más difícil que animar en JavaScript. Si bien la animación CSS tiene algunas limitaciones, la mayoría de las veces es más capaz de lo que creemos. Sin mencionar, típicamente más eficiente.

Junto con un toque de JavaScript, las animaciones y transiciones CSS pueden lograr animaciones e interacciones aceleradas por hardware de manera más eficiente que la mayoría de las bibliotecas de JavaScript.

¡Saltemos directamente!

Nota rápida: las animaciones y las transiciones son diferentes

Las transiciones en CSS se aplican a un elemento y especifican que cuando una propiedad cambia, debe hacerlo gradualmente durante un período de tiempo. Las animaciones son diferentes. Cuando se aplica, simplemente corren y hacen lo suyo. Ofrecen un control más detallado, ya que puede controlar diferentes paradas de las animaciones.

En este artículo, cubriremos cada uno de ellos por separado.

Manipulación de transiciones CSS

Hay innumerables preguntas en los foros de codificación relacionadas con la activación y la pausa de la transición de un elemento. La solución es bastante simple usando JavaScript.

Para desencadenar la transición de un elemento, cambie el nombre de una clase en ese elemento que lo desencadena.

Para pausar la transición de un elemento, use getComputedStyle y getPropertyValue en el punto de la transición en el que desea pausarlo. Luego, establezca esas propiedades CSS de ese elemento iguales a los valores que acaba de obtener.

El siguiente es un ejemplo de ese enfoque.

Esta misma técnica se puede utilizar de formas más avanzadas. El siguiente ejemplo también desencadena una transición al cambiar el nombre de una clase, pero esta vez una variable realiza un seguimiento de la tasa de zoom actual.

Tenga en cuenta que esta vez estamos cambiando el tamaño del fondo. Hay muchas propiedades CSS diferentes que se pueden cambiar o animar, normalmente una que tiene valores numéricos o de color. Rodney Rehm también escribió un artículo particularmente útil e informativo sobre transiciones CSS que se puede encontrar aquí.

Uso de “funciones de devolución de llamada” de CSS

Algunos de los trucos de JavaScript más útiles pero poco conocidos para manipular las transiciones y animaciones CSS son los eventos DOM que activan. Me gusta: animationend, animationstart, y animationiteration para animaciones y transitionend para las transiciones. Puede adivinar lo que hacen. Estos eventos de animación se activan cuando la animación de un elemento finaliza, comienza o completa una iteración, respectivamente.

Estos eventos deben tener el prefijo del proveedor en este momento, por lo que en esta demostración, usamos una función desarrollada por Craig Buckler llamada PrefixedEvent, que tiene los parámetros element, type, y callback para ayudar a que estos eventos se realicen en varios navegadores. Aquí está su útil artículo sobre la captura de animaciones CSS con JavaScript. Y aquí hay otro que determina para qué animación (nombre) se está disparando el evento.

La idea de esta demostración es agrandar el corazón y detener la animación cuando se coloca el cursor sobre él.

La versión pura de CSS es nerviosa. A menos que coloques el cursor sobre él en el momento perfecto, saltará a un estado particular antes de agrandarse al estado flotante final. La versión de JavaScript es mucho más fluida. Elimina el salto dejando que la animación se complete antes de aplicar el nuevo estado.

Manipular animaciones CSS

Como acabamos de aprender, podemos observar elementos y reaccionar a eventos relacionados con la animación: animationStart, animationIteration, y animationEnd. Pero, ¿qué sucede si desea cambiar la animación CSS a mitad de la animación? ¡Esto requiere un poco de engaño!

La propiedad animation-play-state

El animation-play-state propiedad de CSS es increíblemente útil cuando simplemente necesita pausar una animación y potencialmente continuarla más tarde. Puede cambiar ese CSS a través de JavaScript de esta manera (tenga en cuenta sus prefijos):

element.style.webkitAnimationPlayState = "paused";
element.style.webkitAnimationPlayState = "running";

Sin embargo, cuando se pausa una animación CSS usando animation-play-state, se evita que el elemento se transforme de la misma manera que cuando se ejecuta una animación. No puede pausarlo, transformarlo, reanudarlo y esperar que se ejecute con fluidez desde el nuevo estado transformado. Para hacer eso, tenemos que involucrarnos un poco más.

Obtención del porcentaje de valor clave actual

Desafortunadamente, en este momento, no hay forma de obtener el “porcentaje completado” actual exacto de una animación de fotogramas clave CSS. El mejor método para aproximarlo es usar un setInterval función que itera 100 veces durante la animación, que es esencialmente: la duración de la animación en ms / 100. Por ejemplo, si la animación es de 4 segundos, entonces setInterval debe ejecutarse cada 40 milisegundos (4000/100).

var showPercent = window.setInterval(function() {
  if (currentPercent < 100) {
    currentPercent += 1;
  } else {
    currentPercent = 0;
  }
  // Updates a div that displays the current percent
  result.innerHTML = currentPercent;
}, 40);

Este enfoque está lejos de ser ideal porque la función en realidad se ejecuta con menos frecuencia que cada 40 milisegundos. Creo que establecerlo en 39 milisegundos es más preciso, pero confiar en eso es una mala práctica, ya que probablemente varía según el navegador y no se adapta perfectamente a ningún navegador.

Obtención de los valores de propiedad CSS actuales de la animación

En un mundo perfecto, podríamos seleccionar un elemento que esté usando una animación CSS, eliminar esa animación y darle una nueva. Luego comenzaría la nueva animación, comenzando desde su estado actual. No vivimos en ese mundo perfecto, por lo que es un poco más complejo.

A continuación tenemos una demostración para probar una técnica de obtención y cambio de una animación CSS “mid stream”, por así decirlo. La animación mueve un elemento en una ruta circular con la posición de inicio en el centro superior (“doce en punto”, si lo prefiere). Cuando se hace clic en el botón, debe cambiar la posición de inicio de la animación a la ubicación actual del elemento. . Viaja por el mismo camino, solo que ahora “comienza” en la ubicación en la que estaba cuando presionó el botón. Este cambio de origen, y por lo tanto el cambio de animación, se indica cambiando el color del elemento a rojo en el primer fotograma clave.

¡Necesitamos profundizar bastante para hacer esto! Tendremos que profundizar en la propia hoja de estilo para encontrar la animación original.

Puede acceder a las hojas de estilo asociadas con una página utilizando document.styleSheets e iterar a través de él usando un for círculo. A continuación se muestra cómo puede usar JavaScript para encontrar los valores de una animación en particular en una CSSKeyFrameRules objeto:

function findKeyframesRule(rule) {
  var ss = document.styleSheets;
  for (var i = 0; i < ss.length; ++i) {
    for (var j = 0; j < ss[i].cssRules.length; ++j) {
      if (ss[i].cssRules[j].type == window.CSSRule.WEBKIT_KEYFRAMES_RULE && 
      ss[i].cssRules[j].name == rule) { 
        return ss[i].cssRules[j]; }
    }
  }
  return null;
}

Una vez que llamamos a la función anterior (por ejemplo, var keyframes = findKeyframesRule(anim)), puede obtener la duración de la animación del objeto (el número total de cuántos fotogramas clave hay en esa animación) utilizando keyframes.cssRules.length. Luego, necesitamos quitar el “%” de cada uno de los fotogramas clave para que sean solo números y JavaScript pueda usarlos como números. Para hacer esto, usamos lo siguiente, que usa JavaScript .map método.

// Makes an array of the current percent values
// in the animation
var keyframeString = [];  
for(var i = 0; i < length; i ++)
{
  keyframeString.push(keyframes[i].keyText); 
}
  
// Removes all the % values from the array so
// the getClosest function can perform calculations
var keys = keyframeString.map(function(str) {
  return str.replace('%', '');
});

En este punto, keys será una matriz de todos los fotogramas clave de la animación en formato numérico.

Cambiar la animación real (¡finalmente!)

En el caso de nuestra demostración de animación circular, necesitamos dos variables: una para rastrear cuántos grados ha viajado el círculo desde su ubicación de inicio más reciente, y otra para rastrear cuántos grados ha viajado desde la ubicación de inicio original. Podemos cambiar la primera variable usando nuestro setInterval función (usando el tiempo transcurrido y los grados en un círculo). Luego, podemos usar el siguiente código para actualizar la segunda variable cuando se hace clic en el botón.

totalCurrentPercent += currentPercent;
// Since it's in percent it shouldn't ever be over 100
if (totalCurrentPercent > 100) {
  totalCurrentPercent -= 100;
}

Luego, podemos usar la siguiente función para encontrar qué fotograma clave de la animación está más cerca del porcentaje actual total, según la matriz de posibles porcentajes de fotogramas clave que obtuvimos anteriormente.

function getClosest(keyframe) {
  // curr stands for current keyframe
  var curr = keyframe[0];
  var diff = Math.abs (totalCurrentPercent - curr);
  for (var val = 0, j = keyframe.length; val < j; val++) {
    var newdiff = Math.abs(totalCurrentPercent - keyframe[val]);
    // If the difference between the current percent and the iterated 
    // keyframe is smaller, take the new difference and keyframe
    if (newdiff < diff) {
      diff = newdiff;
      curr = keyframe[val];
     }
  }
  return curr;
}

Para obtener el primer valor de fotograma clave de la nueva animación para usar en los cálculos más adelante, podemos usar JavaScript .indexOf método. Luego eliminamos los fotogramas clave originales para poder volver a crear otros nuevos.

for (var i = 0, j = keyframeString.length; i < j; i ++) {
  keyframes.deleteRule(keyframeString[i]);
}

A continuación, necesitamos cambiar el% a un grado del círculo. Podemos hacer esto simplemente multiplicando el nuevo primer porcentaje por 3.6 (porque 10 0 * 3.6 = 360).

Finalmente, creamos las nuevas reglas en base a las variables obtenidas anteriormente. La diferencia de 45 grados entre cada regla se debe a que tenemos 8 fotogramas clave diferentes que giran alrededor del círculo. 360 (grados en un círculo) dividido por 8 es 45.

// Prefix here as needed

keyframes.insertRule("0% { 
  -webkit-transform: translate(100px, 100px) rotate(" + (multiplier + 0) + "deg) 
                     translate(-100px, -100px) rotate(" + (multiplier + 0) + "deg);
  background-color: red; 
}");
keyframes.insertRule("13% { 
  -webkit-transform: translate(100px, 100px) rotate(" + (multiplier + 45) + "deg)
                     translate(-100px, -100px) rotate(" + (multiplier + 45) + "deg); 
}");

// ...continued...

Luego reiniciamos el porcentaje actual setInterval para que pueda ejecutarse de nuevo. Tenga en cuenta que lo anterior tiene el prefijo WebKit. Para hacerlo más compatible con todos los navegadores, posiblemente podría hacer un rastreo de UA para adivinar qué prefijos se necesitarían:

// Gets the browser prefix
var browserPrefix;
navigator.sayswho= (function(){
  var N = navigator.appName, ua = navigator.userAgent, tem;
  var M = ua.match(/(opera|chrome|safari|firefox|msie)/?s*(.?d+(.d+)*)/i);
  if(M && (tem = ua.match(/version/([.d]+)/i))!= null) M[2] = tem[1];
  M = M? [M[1], M[2]]: [N, navigator.appVersion,'-?'];
  M = M[0];
  if(M == "Chrome") { browserPrefix = "webkit"; }
  if(M == "Firefox") { browserPrefix = "moz"; }
  if(M == "Safari") { browserPrefix = "webkit"; }
  if(M == "MSIE") { browserPrefix = "ms"; }
})();

Si desea investigar más a fondo, la respuesta de Russell Uresti en esta publicación de StackOverflow y el ejemplo correspondiente son útiles.

Convertir animaciones en transiciones

Como hemos visto, la manipulación de las transiciones CSS se puede simplificar utilizando JavaScript. Si no obtiene los resultados que desea con las animaciones CSS, puede intentar convertirlo en una transición y trabajar con él de esa manera. Tienen aproximadamente la misma dificultad de codificar, pero pueden configurarse y editarse más fácilmente.

El mayor problema al convertir animaciones CSS en transiciones es cuando cambiamos animation-iteration en el equivalente transition mando. La transición no tiene equivalente directo, por lo que son cosas diferentes en primer lugar.

Relacionando esto con nuestra demostración de rotación, un pequeño truco es multiplicar tanto transition-duration y el rotation por x. Luego, debe tener / aplicar una clase para activar la animación, porque si aplica las propiedades cambiadas directamente al elemento, bueno, no habrá mucha transición. Para iniciar la transición (animación falsa), aplica la clase al elemento.

En nuestro ejemplo, lo hacemos al cargar la página:

Manipulación de matrices CSS

La manipulación de animaciones CSS también se puede realizar mediante el uso de un CSSMatrix. Por ejemplo:

var translated3D = 
  new WebKitCSSMatrix(window.getComputedStyle(elem, null).webkitTransform); 

Pero el proceso puede resultar confuso, especialmente para aquellos que recién comienzan a usar animaciones CSS.

Para obtener más información sobre las matrices CSS, consulte la documentación (aunque es cierto que no es demasiado útil), esta herramienta que le permite jugar con valores matriciales o artículos sobre el tema, como el que se muestra aquí.

Restablecimiento de animaciones CSS

El truco para hacer esto de la manera correcta se puede encontrar aquí en Programar Plus. El truco es esencialmente (si es posible) eliminar la clase que inició la animación, activar el reflujo de alguna manera y luego aplicar la clase nuevamente. Si todo lo demás falla, extraiga el elemento de la página y vuelva a colocarlo.

Usa tu cabeza

Antes de comenzar a codificar, pensar y planificar cómo debe ejecutarse una transición o animación es la mejor manera de minimizar sus problemas y obtener el efecto que desea. ¡Incluso mejor que buscar soluciones en Google más adelante! Es posible que las técnicas y los trucos que se describen en este artículo no siempre sean la mejor manera de crear la animación que requiere su proyecto.

Aquí hay un pequeño ejemplo de cómo ser inteligente con HTML y CSS por sí solo puede resolver un problema en el que podría haber pensado en utilizar JavaScript.

Supongamos que queremos que un gráfico gire continuamente y luego cambie la dirección de rotación cuando se desplaza. Al aprender lo que se cubrió en este artículo, es posible que desee comenzar y usar un animationIteration evento para cambiar la animación. Sin embargo, se puede encontrar una solución más eficiente y de mejor rendimiento utilizando CSS y un elemento contenedor agregado.

El truco consistiría en hacer girar la espiral en x velocidad en una dirección y, cuando se mantiene suspendido, hace que el elemento principal gire en 2x velocidad en la dirección opuesta (comenzando en la misma posición). Las dos rotaciones que trabajan una contra la otra crean un efecto neto de la espiral girando en la dirección opuesta.

El mismo concepto se usó en este ejemplo para una pregunta de StackOverflow.

Cosas relacionadas que pueden resultarle interesantes.

  • Animo.js: “Una pequeña herramienta poderosa para administrar animaciones CSS”
  • ¡Gracias a Dios tenemos una especificación! – Artículo de la revista Smashing sobre peculiaridades de la transición

En resumen

  • getComputedStyle es útil para manipular las transiciones CSS.
  • transitionend y sus eventos relacionados son bastante útiles al manipular transiciones y animaciones CSS usando JavaScript.
  • Se puede cambiar una animación CSS de sus valores actuales obteniendo las hojas de estilo en JavaScript, pero puede ser bastante complicado.
  • En JavaScript, las transiciones CSS son generalmente más fáciles de trabajar que las animaciones CSS.
  • Las matrices CSS son generalmente difíciles de manejar, especialmente para los principiantes.
  • Pensar en lo que se debe hacer y planificar cómo hacerlo es esencial en la codificación de animaciones.
(Visited 4 times, 1 visits today)