19/02/2023
En el vibrante mundo del desarrollo de juegos y aplicaciones web, las animaciones CSS son una herramienta fundamental para crear experiencias de usuario dinámicas, atractivas e inmersivas. Hacen que los elementos aparezcan, desaparezcan, se muevan y cambien de forma, aportando vida a interfaces que de otro modo serían estáticas. Sin embargo, a menudo nos enfrentamos a un desafío crucial: ¿cómo podemos saber desde nuestro código JavaScript cuándo una de estas animaciones ha comenzado, ha completado un ciclo o ha finalizado por completo? La respuesta reside en los listeners de animación, un mecanismo poderoso que nos permite sincronizar nuestra lógica de JavaScript con el ciclo de vida de las animaciones CSS.

Aunque el término "AnimationListener" es más común en otros entornos de desarrollo como Android (Java/Kotlin), en el contexto de la web y JavaScript, este concepto se implementa a través de los eventos de animación estándar. Estos eventos actúan como puentes de comunicación entre nuestras hojas de estilo (CSS) y nuestros scripts (JS), permitiéndonos ejecutar funciones específicas en momentos clave. Dominar estos listeners es esencial para orquestar secuencias complejas, gestionar estados en un juego, o simplemente limpiar elementos del DOM después de que hayan cumplido su propósito visual.
¿Qué es Realmente un "Listener de Animación" en JavaScript?
Un listener de animación en JavaScript no es una clase o un objeto específico llamado AnimationListener. En cambio, es una función (conocida como "callback" o "handler") que nosotros definimos y que se ejecuta automáticamente cuando ocurre un evento de animación específico en un elemento del DOM. Para que esto funcione, el elemento debe tener una animación CSS aplicada.

El navegador dispara tres eventos principales relacionados con las animaciones, que conforman la interfaz AnimationEvent:
- animationstart: Se dispara en el instante en que la animación CSS comienza a ejecutarse. Es ideal para preparar el terreno, como deshabilitar un botón mientras dura su animación de "carga".
- animationend: Posiblemente el más utilizado. Se dispara una vez que la animación ha completado su curso. Es perfecto para realizar acciones de limpieza, como eliminar un enemigo de la pantalla después de su animación de explosión, o para encadenar la siguiente acción en una secuencia.
- animationiteration: Se dispara al final de cada iteración de una animación que se repite (es decir, que tiene un
animation-iteration-countmayor que 1). No se dispara en la última iteración, ya que en ese punto se disparaanimationend. Es útil para cambiar algo en cada ciclo de una animación en bucle.
El Objeto AnimationEvent: La Información que Recibimos
Cuando nuestro listener (la función) es llamado, recibe como argumento un objeto de evento. Este objeto, una instancia de AnimationEvent, contiene información valiosa sobre el evento que acaba de ocurrir. Además de las propiedades estándar de cualquier evento (como target, el elemento que disparó el evento), nos proporciona detalles específicos de la animación.
A continuación, una tabla con las propiedades más importantes del objeto AnimationEvent:
| Propiedad | Descripción | Ejemplo de Valor |
|---|---|---|
animationName | Una cadena de texto con el valor de la propiedad CSS animation-name que generó el evento. | 'fadeIn', 'playerExplosion' |
elapsedTime | Un número flotante que indica el tiempo en segundos que la animación ha estado ejecutándose cuando se disparó el evento. Para animationstart, suele ser 0.0, a menos que haya un animation-delay negativo. | 3.5 (para una animación de 3.5s al dispararse animationend) |
pseudoElement | Una cadena de texto que identifica el pseudo-elemento en el que se ejecuta la animación (ej. '::before'). Si la animación se ejecuta sobre el elemento principal, esta cadena estará vacía. | '::after' o '' |
type | El tipo de evento que se ha disparado. | 'animationstart', 'animationend' |
La propiedad elapsedTime es particularmente útil. Nos permite saber con precisión cuánto tiempo ha transcurrido, lo cual es crucial para lógicas de juego que dependen del tiempo o para sincronizar múltiples elementos visuales.

Implementación Práctica: Añadiendo un Listener a un Elemento
Existen dos formas principales de asignar un listener de animación a un elemento. La primera y más recomendada es utilizando el método addEventListener().
Método 1: Usando addEventListener() (Recomendado)
Este es el método moderno y más flexible. Permite añadir múltiples listeners para el mismo evento en un solo elemento, lo cual es excelente para mantener el código modular y organizado.
Veamos un ejemplo de un juego: una moneda que desaparece después de una animación de "recogida".

<!-- HTML --> <div id="coin" class="coin-item"></div> <style> /* CSS */ .coin-item { width: 50px; height: 50px; background-color: gold; border-radius: 50%; } .collected { animation-name: collect-animation; animation-duration: 0.5s; animation-fill-mode: forwards; /* Mantiene el estado final */ } @keyframes collect-animation { 0% { transform: translateY(0) scale(1); opacity: 1; } 100% { transform: translateY(-100px) scale(0); opacity: 0; } } </style> <script> // JavaScript const coin = document.getElementById('coin'); // Función que se ejecutará cuando la animación termine function onAnimationEndHandler(event) { console.log(`La animación "${event.animationName}" ha terminado.`); // Elimina la moneda del DOM para liberar recursos coin.remove(); } // Cuando el jugador "recoge" la moneda (ej. al hacer clic) coin.addEventListener('click', () => { // Añadimos la clase que dispara la animación coin.classList.add('collected'); // Añadimos el listener para el evento 'animationend' coin.addEventListener('animationend', onAnimationEndHandler); }); </script>Método 2: Usando Propiedades `onanimationend`
Esta es una forma más antigua, donde se asigna una función directamente a la propiedad del evento en el elemento. Su principal desventaja es que solo puedes tener un listener por evento en cada elemento; si asignas una nueva función, la anterior se sobrescribe.
// JavaScript (alternativa) const animatedElement = document.querySelector('.animated'); animatedElement.onanimationend = () => { console.log('La animación ha terminado (método de propiedad).'); // Aquí iría la lógica a ejecutar }; // ¡Cuidado! Si haces esto, la función anterior se pierde: // animatedElement.onanimationend = () => { console.log('Nueva función'); };Tabla Comparativa de Métodos
| Característica | addEventListener() | Propiedad on... |
|---|---|---|
| Múltiples Listeners | Sí, permite varios por evento. | No, solo uno por evento. |
| Flexibilidad | Alta. Permite usar removeEventListener y opciones avanzadas. | Baja. Más simple, pero menos potente. |
| Práctica Recomendada | Sí | No, se considera obsoleto para código nuevo. |
Casos de Uso Avanzados en Desarrollo de Juegos
Los listeners de animación son el pegamento que une la presentación visual con la lógica del juego. Aquí algunos ejemplos:
- Secuencias de Combate: Un personaje realiza una animación de ataque. El listener
animationendse usa para registrar el daño al oponente justo cuando el golpe visualmente conecta, no antes. - Indicadores de Daño: Cuando un jugador recibe daño, aparece un número flotante que se desvanece. Se usa
animationendpara eliminar el elemento del número del DOM una vez que ha desaparecido. - Transiciones de Nivel: Al completar un nivel, se puede aplicar una animación de "fade out" a toda la pantalla. El listener
animationendes la señal para detener el bucle del juego actual y cargar los recursos del siguiente nivel. - Tutoriales Interactivos: Una mano animada señala un botón. Después de que la animación de señalar termina (
animationend), el juego espera la interacción del usuario.
Preguntas Frecuentes (FAQ)
¿Cuál es la diferencia entre AnimationEvent y TransitionEvent?
Ambos son similares, pero responden a diferentes propiedades de CSS. AnimationEvent se dispara por la propiedad animation, que permite secuencias complejas con múltiples pasos (keyframes). TransitionEvent se dispara por la propiedad transition, que define un cambio suave entre dos estados (ej. de opacity: 0 a opacity: 1).

¿Por qué mi evento animationend no se dispara?
Las causas más comunes son: 1) El nombre del evento está mal escrito (es animationend, todo en minúsculas). 2) La animación tiene una duración de 0 segundos. 3) La animación se interrumpe antes de terminar, por ejemplo, si el elemento se oculta (display: none) o se elimina del DOM. 4) La propiedad animation-iteration-count está establecida en infinite; en este caso, animationend nunca se disparará.
¿Cómo puedo eliminar un listener una vez que ya no lo necesito?
Si usaste addEventListener, puedes usar removeEventListener. Es una buena práctica hacerlo para evitar fugas de memoria, especialmente en aplicaciones de una sola página (SPA) o juegos donde los elementos se crean y destruyen constantemente. Para que esto funcione, la función del listener no puede ser anónima; debe ser una función con nombre o una referencia a una función.
// Para poder eliminarlo, la función no debe ser anónima function miListener() { console.log('Animación terminada'); // Una vez que se ejecuta, se auto-elimina elemento.removeEventListener('animationend', miListener); } elemento.addEventListener('animationend', miListener);Si quieres conocer otros artículos parecidos a Listeners de Animación en JavaScript: La Guía puedes visitar la categoría Juegos.
