Ir al contenido
angel.library

GSAP

Animaciones imperativas con control fino (timelines, secuencias, scroll) que CSS puro no cubre bien. En un proyecto Astro corre normalmente en un <script> de cliente o dentro directamente isla.

Ventana de terminal
pnpm add gsap
import gsap from "gsap"
gsap.to(".card", { opacity: 1, y: 0, duration: 0.5 })
gsap.from(".card", { opacity: 0, y: 20 }) // anima DESDE ese estado hasta el actual
gsap.fromTo(".card", { opacity: 0 }, { opacity: 1, duration: 0.5 })

Una timeline encadena tweens en orden, sin manejar delay a mano para cada uno.

const tl = gsap.timeline()
tl.from(".titulo", { opacity: 0, y: 20 })
.from(".subtitulo", { opacity: 0, y: 20 }, "-=0.3") // arranca 0.3s antes de que termine el anterior
.from(".cta", { opacity: 0, scale: 0.9 })

Plugin oficial, hay que registrarlo antes de usarlo. Liga el progreso (o el disparo) directamente animación a la posición de scroll de un elemento.

import gsap from "gsap"
import { ScrollTrigger } from "gsap/ScrollTrigger"
gsap.registerPlugin(ScrollTrigger)
gsap.from(".seccion", {
opacity: 0,
y: 40,
scrollTrigger: {
trigger: ".seccion",
start: "top 80%" // cuando el top de .seccion llega al 80% del viewport
}
})

Scrub (la animación sigue el scroll en vez de dispararse una vez):

gsap.to(".imagen", {
scale: 1.3,
scrollTrigger: {
trigger: ".imagen",
start: "top bottom",
end: "bottom top",
scrub: true
}
})
<div class="card">...</div>
<script>
import gsap from "gsap"
gsap.from(".card", { opacity: 0, y: 20, duration: 0.5 })
</script>

Limpiar animaciones al navegar (View Transitions)

Sección titulada «Limpiar animaciones al navegar (View Transitions)»

Con <ClientRouter /> activo, el DOM se reemplaza pero los ScrollTrigger viejos no se destruyen solos — quedan escuchando un elemento que ya no existe. Hay que matarlos en astro:before-swap.

<script>
import gsap from "gsap"
import { ScrollTrigger } from "gsap/ScrollTrigger"
gsap.registerPlugin(ScrollTrigger)
function animar() {
gsap.from(".seccion", {
opacity: 0,
scrollTrigger: { trigger: ".seccion", start: "top 80%" }
})
}
animar()
document.addEventListener("astro:after-swap", animar)
document.addEventListener("astro:before-swap", () => {
ScrollTrigger.getAll().forEach((trigger) => trigger.kill())
})
</script>
APIUso
gsap.to(target, vars)Animar hacia un estado
gsap.from(target, vars)Animar desde un estado hasta el actual
gsap.timeline()Encadenar varios tweens en secuencia
gsap.registerPlugin(ScrollTrigger)Habilitar animaciones ligadas al scroll
scrollTrigger: { trigger, start, scrub }Configurar cuándo/cómo dispara según scroll
ScrollTrigger.getAll().forEach(t => t.kill())Limpiar triggers viejos antes de un swap de página

Ciclo de vida, rendimiento y movimiento reducido

Sección titulada «Ciclo de vida, rendimiento y movimiento reducido»
  • Sin matar los ScrollTrigger en astro:before-swap, cada navegación con View Transitions activas acumula listeners fantasma sobre elementos que ya no están en el DOM — fugas de memoria progresivas en una SPA-like de varias páginas.
  • El posicionamiento start/end de ScrollTrigger usa “posición del trigger” + “posición del viewport” ("top 80%" = cuando el top del elemento llega al 80% de la altura del viewport) — no son porcentajes de scroll de la página.
  • Para animaciones simples de entrada (fade, slide) sin scroll, la alternativa nativa sin dependencia es animation-timeline: view() en CSS — ver Animaciones CSS. GSAP vale la pena cuando necesitas timelines complejas o control que CSS no da.