Proyectos de Scroll Animado

Exploración de animaciones avanzadas con GSAP y técnicas de scroll interactivo

Descripción General de los Proyectos

Esta colección representa mi evolución en el dominio de animaciones de scroll, desde técnicas básicas con JavaScript vanilla hasta implementaciones avanzadas con GSAP. Cada proyecto explora diferentes enfoques para crear experiencias interactivas que responden al desplazamiento del usuario.

El primer proyecto Moto Scroll me enseñó los fundamentos del método de frames, que posteriormente apliqué en AirPods Scroll. Finalmente, GTA-6 Landing representa el dominio de GSAP y sus capacidades avanzadas de timeline y ScrollTrigger.

GSAP ScrollTrigger JavaScript Frame Animation

Técnicas aprendidas gracias a @midudev

Tecnologías y Enfoques

  1. GSAP (GreenSock Animation Platform): La biblioteca más potente para animaciones web. Utilizada en el proyecto GTA-6 para crear efectos de scroll sincronizados con Timeline y ScrollTrigger, permitiendo animaciones complejas y fluidas.

  2. ScrollTrigger Plugin: Plugin esencial de GSAP que vincula animaciones al scroll de la página. Permite crear efectos como parallax, fade-in/out y transformaciones basadas en la posición del scroll del usuario.

  3. Técnica de Frame Animation: Método desarrollado inicialmente en Moto Scroll y perfeccionado en AirPods. Consiste en cambiar imágenes secuencialmente basándose en el progreso del scroll, creando la ilusión de video interactivo.

  4. Canvas y Renderizado Optimizado: Implementación de canvas HTML5 para renderizar secuencias de imágenes de manera eficiente, optimizando el rendimiento y la fluidez de las animaciones.

Lo que Aprendí

GSAP y Timeline

  • Dominio de gsap.timeline() para secuenciar animaciones complejas
  • Uso de parámetros temporales como "-=1" y "<" para sincronización precisa
  • Implementación del parámetro "scrub" para animaciones bidireccionales

Técnicas de Scroll

  • Desarrollo del método de frame animation desde cero
  • Optimización de rendimiento en animaciones de scroll intensivas
  • Creación de experiencias inmersivas que responden al usuario

Implementación Técnica

GSAP Timeline con Parámetros Temporales

El poder de GSAP reside en su sistema de timeline que permite controlar la sincronización precisa de múltiples animaciones. Los parámetros como "-=1" y "<" son fundamentales para crear secuencias fluidas.

// Registro de plugins esenciales
import gsap from 'gsap';
import { ScrollTrigger } from 'gsap/ScrollTrigger';
gsap.registerPlugin(ScrollTrigger);

// Timeline con parámetros temporales avanzados
const tl = gsap.timeline({
  scrollTrigger: {
    trigger: '.hero-section',
    start: 'top center',
    end: 'bottom top',
    scrub: true // Clave para animaciones bidireccionales
  }
});

// Secuencia de animaciones sincronizadas
tl.to('.title', { opacity: 1, y: 0, duration: 1 })
  .to('.subtitle', { opacity: 1, x: 100, duration: 1 }, '-=0.5') // Inicia 0.5s antes
  .to('.image', { scale: 1.2, rotation: 10, duration: 1 }, '<') // Inicia simultáneamente
  .to('.cta', { opacity: 1, y: -50, duration: 0.8 }, '+=0.2'); // Inicia 0.2s después
            

Técnica de Frame Animation

Desarrollé esta técnica en Moto Scroll y la perfeccioné en AirPods. Consiste en cambiar imágenes secuencialmente basándose en el progreso del scroll, creando una animación fluida y controlada por el usuario.

// Sistema de frame animation optimizado
const canvas = document.querySelector('#frame-canvas');
const ctx = canvas.getContext('2d');
const frameCount = 150; // Total de frames
const images = [];

// Precargar todas las imágenes
for (let i = 1; i <= frameCount; i++) {
  const img = new Image();
  img.src = `./frames/frame_${i.toString().padStart(4, '0')}.jpg`;
  images.push(img);
}

// Función de renderizado basada en scroll
function updateFrame() {
  const scrollTop = window.pageYOffset;
  const maxScroll = document.body.scrollHeight - window.innerHeight;
  const scrollFraction = scrollTop / maxScroll;
  
  // Calcular frame actual basado en progreso de scroll
  const frameIndex = Math.min(
    frameCount - 1,
    Math.floor(scrollFraction * frameCount)
  );
  
  // Renderizar frame actual
  if (images[frameIndex] && images[frameIndex].complete) {
    ctx.clearRect(0, 0, canvas.width, canvas.height);
    ctx.drawImage(images[frameIndex], 0, 0, canvas.width, canvas.height);
  }
}

// Listener optimizado con throttling
let ticking = false;
window.addEventListener('scroll', () => {
  if (!ticking) {
    requestAnimationFrame(() => {
      updateFrame();
      ticking = false;
    });
    ticking = true;
  }
});