Admítelo: tú también has pulsado mil veces a un botón de "Like" solo porque, al hacerlo, soltaba una explosión de color o una lluvia de corazones. No te culpo, a mí también me ha pasado. Son este tipo de microinteracciones las que nos generan esa sensación tan satisfactoria de que la interfaz está viva.

Este detalle, que puede parecer una tontería, es precisamente lo que separa un producto digital bien cuidado y pensado de una aplicación genérica y aburrida.

Ojo, tampoco hace falta llenar tu web de animaciones innecesarias. Pero cuando las usas en el lugar justo (como en ese botón de acción principal) consigues algo muy potente: que el usuario reciba un feedback inmediato y satisfactorio.
Y en realidad, hacer este tipo de animaciones es mucho más sencillo de lo que parece. Así que en el post de hoy vamos a hacer, paso a paso, una microinteracción de partículas en un botón de like.

transición botón de like a liked

¿Qué herramientas vamos a necesitar?

Para este proyecto usaremos HTML, CSS y JavaScript, pero el verdadero motor de la animación será GSAP.

Si no has oído hablar de GSAP, es una de las herramientas más conocidas para animar casi cualquier cosa en la web. Nos ahorra un montón de tiempo (y de líneas de código) en comparación con la forma nativa de hacerlo.

He preparado un ejemplo en CodePen para que puedas probarlo y ver el resultado final ahora mismo, pero aún así lo vamos a ver paso a paso. Puedes ver el ejemplo pulsando sobre el siguiente enlace: Like Button (GSAP)

Estructura HTML: definiendo la estructura semántica del botón

Para empezar, vamos a configurar una estructura básica de HTML. Lo más importante aquí, además de enlazar nuestros archivos locales de estilos y lógica, es importar las librerías necesarias para que la animación de las partículas sea posible.

<html>
  <head>
    <meta charset="UTF-8">
    <link rel="stylesheet" href="./style.css">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Like Button</title>
  </head>
  <body>
    <!-- GSAP -->
    <script src="https://unpkg.com/gsap@3/dist/gsap.min.js"></script>
    <script src="https://assets.codepen.io/16327/Physics2DPlugin3.min.js"></script>

    <!-- SCRIPT -->
    <script src="./script.js"></script>
  </body>
</html>

Para que este efecto funcione correctamente, necesitamos importar dos piezas clave del ecosistema de GSAP:

Nota: el plugin Physics2D es una herramienta premium de GSAP. En este ejemplo lo utilizamos mediante la URL específica para uso en CodePen, pero requiere una licencia si tienes pensado implementarlo en un proyecto comercial.

Ahora, dentro de nuestro <body> (y siempre antes de las etiquetas <script>), añadiremos la estructura del botón.

<button id="like-btn">
  <div id="emitter">
    <svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" stroke-width="2" stroke="currentColor">
      <path stroke-linecap="round" stroke-linejoin="round" d="M21 8.25c0-2.485-2.099-4.5-4.688-4.5-1.935 0-3.597 1.126-4.312 2.733-.715-1.607-2.377-2.733-4.313-2.733C5.1 3.75 3 5.765 3 8.25c0 7.22 9 12 9 12s9-4.78 9-12Z" />
    </svg>
  </div>
  <span>Like</span>
</button>

En este esquema hemos definido dos identificadores importantes:

Estilos CSS: preparando la estética y el contenedor

Para definir los estilos, crearemos un archivo llamado style.css. El primer paso será importar la tipografía y definir una serie de variables CSS (Custom Properties). Esto nos permitirá gestionar los colores y valores reutilizables desde un solo lugar, haciendo que nuestro diseño sea mucho más fácil de mantener.

@import url('https://fonts.googleapis.com/css2?family=DM+Sans:ital,opsz,wght@0,9..40,100..1000;1,9..40,100..1000&display=swap');

:root{
  --color-bg: #e9ecef;
  --color-primary: #495057;
  --color-accent: #ff3562;
  --color-on-accent: #ffffff;

  --text-sm: 1.1em;
  --text-md: 1.8rem;
    
  --transition-default: all 0.25s ease;
}

A continuación definiremos el estilo global para limpiar márgenes y centrar nuestro botón en pantalla.

*{
  font-family: "DM Sans", sans-serif;
}

body,
html {
  height: 100%;
  margin: 0;
  padding: 0;
  overflow: hidden;
}

body {
  display: flex;
  align-items: center;
  justify-content: center;
  flex-direction: column;
  min-height: 100vh;
    background-color: var(--color-bg);
}

Además, prepararemos la clase .particles-container. Aunque este contenedor lo crearemos dinámicamente en JavaScript, por claridad del código, el estilo lo definiremos aquí:

.particles-container {
  position: absolute;
  left: 0;
  top: 0;
  overflow: visible;
  z-index: 2;
  pointer-events: none;
  opacity: 0;
}

En la clase .particles-container, hay un par de propiedades que merecen especial atención:

El siguiente paso es definir el estilo de nuestro botón. Verás que aquí hacemos uso de las variables CSS que definimos al principio, lo que nos permite mantener el código más limpio y coherente.

Además, prepararemos el estado .clicked. Esta clase la añadiremos mediante JavaScript en el momento del clic para modificar el estilo del botón y sus elementos internos (como el icono y el texto):

button{
  border-radius: 8px;
  cursor: pointer;
  padding: 12px 26px;
  font-size: var(--text-md);
  border: 2px solid var(--color-primary);
  display: flex;
  align-items: center;
  gap: 12px;
  color: var(--color-primary);
  background-color: #e9ecef;
  transition: var(--transition-default);
}

button.clicked{
  border-color: var(--color-accent);
  background-color: var(--color-accent);
  color: var(--color-on-accent);
}

button svg{
  width: var(--text-sm);
  fill: var(--color-primary);
  stroke: var(--color-primary);
  transition: var(--transition-default);
}

button.clicked svg{
  fill: var(--color-on-accent);
  stroke: var(--color-on-accent);
}

Por último, necesitamos definir el aspecto de la partícula. En este ejemplo se ha optado por un diseño de puntos circulares. No obstante, este estilo lo puedes ajustar como más te guste.

.dot {
  position: absolute;
  border-radius: 50%;
}

Lógica con JavaScript: creando el sistema de partículas con GSAP

Es hora de pasar a la acción. Ahora configuraremos la lógica necesaria para que el botón responda a nuestros clics y genere la explosión de partículas. Todo este proceso lo gestionaremos desde nuestro archivo script.js.

Paso 1. Registrar los plugins de GSAP

Lo primero que debemos hacer es registrar el plugin al motor principal de GSAP. Esto es fundamental para que la librería reconozca las propiedades físicas que vamos a usar más adelante.

gsap.registerPlugin(Physics2DPlugin);

Paso 2. Declarar los elementos

En este paso vamos a capturar los elementos del DOM con los que vamos a interactuar. También aprovecharemos para crear, de forma dinámica, el contenedor donde vivirán nuestras partículas:

// Main emitter element (used as explosion origin)
const emitter = document.getElementById("emitter");

// Like button + text
const likeBtn = document.getElementById("like-btn");
const text = likeBtn.querySelector("span");

// Container that holds all particles
const container = document.createElement("div");
container.className = "particles-container";
document.body.appendChild(container);

Paso 3. Configuración de GSAP y estados

En este punto definiremos las variables que controlan el comportamiento de nuestra animación. Lo interesante de este enfoque es que, simplemente modificando estos valores, puedes transformar por completo el efecto.

Te animo a que pruebes a cambiar los parámetros como la velocidad o la gravedad para ver cómo cambia la física del movimiento. Además, en este paso, aprovecharemos para declarar el estado inicial del botón:

const emitterSize = 2;        // spawn area radius
const dotQuantity = 12;     // number of particles
const dotSizeMax = 12;      // max particle size
const dotSizeMin = 4;       // min particle size
const speed = 0.6;            // initial velocity multiplier
const gravity = 0.1;          // gravity strength

// Toggle state (liked / not liked)
let liked = false;

Paso 4. Configuración de la explosión

En este paso crearemos una función reutilizable que se encargará de generar la "explosión" cada vez que sea llamada. La idea es crear un timeline de GSAP que contenga las animaciones individuales de cada partícula.

const explosion = createExplosion(container);

function createExplosion(container) {
  const tl = gsap.timeline({ paused: true });

  for (let i = 0; i < dotQuantity; i++) {

    // Create particle element
    const dot = document.createElement("div");
    dot.className = "dot";

    // Assign random color
    const colors = ["#ff4d6d", "#fb6f92", "#ff8fab", "#ffb3c1", "#cdb4db", "#a2d2ff"];
    dot.style.backgroundColor = colors[Math.floor(Math.random() * colors.length)];

    container.appendChild(dot);

    // Random size & direction (angle in radians)
    const size = gsap.utils.random(dotSizeMin, dotSizeMax, 1);
    const angle = Math.random() * Math.PI * 2;

    // Initial offset inside emitter radius
    const length = Math.random() * (emitterSize / 2 - size / 2);


    gsap.set(dot, {
      x: Math.cos(angle) * length,
      y: Math.sin(angle) * length,
      width: size,
      height: size,
      xPercent: -50,
      yPercent: -50,
      force3D: true,
      scale: gsap.utils.random(0.8, 1.2)
    });

    tl.to(
      dot,
      {
        physics2D: {
          angle: (angle * 180) / Math.PI, // convert to degrees
          velocity: (120 + Math.random() * 200) * speed,
          gravity: 500 * gravity
        },
        duration: 1 + Math.random()
      },
      0
    )
    .to(
      dot,
      {
        opacity: 0,
        scale: 0.5,
        duration: 0.3,
        ease: "power2.in",
      },
      0.3
    );
  }

  return tl;
}

En esta función hay algunos conceptos técnicos que debemos entender:

  1. Bucle de creación. Usamos un for basado en la variable dotQuantity para generar todas las partículas de golpe. Cada una recibe un color y un tamaño aleatorio para que la explosión no parezca artificial ni repetitiva.
  2. Cálculo de trayectoria. Utilizamos funciones matemáticas (Math.cos y Math.sin) para posicionar las partículas en un círculo alrededor del punto central. Es lo que permite que salgan disparadas en todas las direcciones (360 grados).
  3. El poder de Physics2D. En lugar de animar manualmente las coordenadas x e y, simplemente le pasamos al plugin un ángulo, una velocidad y una gravedad. GSAP se encarga de dibujar la parábola perfecta de caída.
  4. Sincronización (parámetros de posición). Verás que las funciones .to() tienen un 0 o un 0.3 al final. En GSAP, esto se llama Position Parameter.

Paso 5. Función de la explosión

Una vez que tenemos la lógica de la animación preparada, necesitamos una función que "coloque" el contenedor de partículas en el lugar correcto y active el movimiento. Sin este paso, las partículas podrían aparecer en una esquina de la pantalla en lugar de nacer desde el centro del botón.

function explode(element) {
  const bounds = element.getBoundingClientRect();

  // Move container to element center
  gsap.set(container, {
    x: bounds.left + bounds.width / 2,
    y: bounds.top + bounds.height / 2,
    opacity: 1
  });

  // Restart animation from the beginning
  explosion.restart();
}

Esta función se encarga de posicionar el efecto en el espacio: primero, utilizamos getBoundingClientRect() para calcular las coordenadas exactas del botón en la pantalla y, mediante gsap.set(), movemos instantáneamente el contenedor de partículas a su centro.

Finalmente, ejecutamos explosion.restart() para que el timeline de la animación vuelva al inicio y se reproduzca desde cero con cada nuevo clic, asegurando que la explosión nazca siempre desde el lugar correcto.

Paso 6. Evento de clic e interacción final

Para terminar, necesitamos detectar cuándo el usuario interactúa con el botón y disparar toda la lógica que hemos programado. Con el siguiente código, gestionamos el cambio de estado, la actualización visual y el lanzamiento de las partículas:

likeBtn.addEventListener("click", () => {

  // Toggle like state
  liked = !liked;
  text.textContent = liked ? "Liked" : "Like";

  // Toggle active class (for styling)
  likeBtn.classList.toggle("clicked", liked);

  // Button press animation (quick scale feedback)
  gsap.fromTo(
    likeBtn,
    { scale: 1 },
    { scale: 0.9, duration: 0.1, yoyo: true, repeat: 1 }
  );

  // Trigger particle explosion
  liked && explode(emitter);
});

En este bloque final ocurren dos cosas clave:

Espero que este post te haya ayudado a entender mejor cómo funcionan este tipo de efectos y que, a partir de ahora, te sientas capaz de implementar tus propias animaciones de partículas para dar un toque extra de "magia" a tus interfaces. ¡A experimentar!

Cuéntanos qué te parece.

Los comentarios serán moderados. Serán visibles si aportan un argumento constructivo. Si no estás de acuerdo con algún punto, por favor, muestra tus opiniones de manera educada.

Suscríbete