Retour au catalogue

Video Scroll Play

Progression simulee au scroll avec barre circulaire SVG qui se remplit. Le contenu change selon la progression.

videocomplex Both Responsive a11y
minimalcorporateuniversalsaasagencystickysplit
Theme

Cómo crear una sección React con progreso por pasos guiado por el scroll

Una sección React con pasos guiada por el scroll fija un viewport con `position: sticky` a lo largo de un contenedor desplazable alto, convierte el avance del scroll en un trazo SVG circular mediante useScroll y useTransform de Framer Motion, y activa cada tarjeta de paso cuando la fracción de scroll alcanza el umbral correspondiente.

  • Stack: React 18 + Framer Motion 11 + Tailwind v4, ~270 líneas, cero dependencias adicionales.
  • API principal: useScroll (target + offset), useTransform, motion.circle con strokeDashoffset.
  • La altura del contenedor se adapta al número de pasos (100vh por paso, mínimo 200vh) para que la distancia de scroll corresponda directamente con el progreso.
  • Accesible: el contenido de los pasos siempre está en el DOM; el anillo y los marcadores son SVG decorativo sin ARIA faltante.
  • Funciona en móvil: el scroll está disponible en todos los dispositivos, sin necesidad de puntero.

Video Scroll Play es una sección React sticky que convierte la barra de scroll en un indicador de progreso. A medida que el usuario recorre un contenedor alto, un anillo SVG circular se rellena del 0 al 100% y cada tarjeta de paso se ilumina en secuencia. Es un patrón habitual en los flujos de onboarding y las secciones "cómo funciona" de producto, donde la velocidad de lectura no debe imponer el ritmo.

Anatomía

El `<section>` exterior es el objetivo del scroll; su altura se calcula a partir del número de pasos para que cada paso ocupe una altura de viewport. Dentro, un div `position: sticky` se fija a la parte superior del viewport en todo momento y ocupa toda la pantalla. Ese div sticky contiene una cuadrícula de dos columnas: la columna izquierda lleva el título, el subtítulo y una lista de tarjetas de paso; la columna derecha es un SVG de 280px que dibuja un anillo decorativo exterior, un círculo de pista y un arco de progreso animado. Los marcadores de paso (puntos de 8px en el anillo exterior) pasan del color de borde al color de acento a medida que avanza el scroll.

Cómo funciona

useScroll recibe la ref de la sección y un offset `['start start', 'end end']`, produciendo un scrollYProgress de 0 a 1 que cubre todo el contenedor. useTransform mapea ese valor a strokeDashoffset: la circunferencia del círculo (2πr con r=54) se convierte en el dashoffset inicial y decrementa hasta 0 a medida que la página se desplaza. Un segundo useTransform convierte el mismo rango 0-1 en un porcentaje de 0 a 100 mostrado en el centro del anillo. El resaltado de pasos usa otro useTransform que mapea linealmente la fracción de scroll a un índice de paso, luego cada tarjeta comprueba si Math.round(currentIndex) coincide con su propio índice para alternar la opacidad, el color de borde y el fondo.

Cómo crearlo en React

  1. Configurar el contenedor de scroll y el viewport sticky

    Da a la sección exterior una altura proporcional al número de pasos para que cada paso tenga su propia ventana de scroll. Anida dentro un div `position: sticky; top: 0; height: 100vh`, este es el único elemento que el usuario ve.

    const sectionRef = useRef<HTMLElement>(null);
    // 100vh per step, min 200vh
    const sectionHeight = `${Math.max(200, steps.length * 100)}vh`;
  2. Conectar el progreso del scroll al arco SVG

    Pasa la ref de la sección a useScroll con el offset `['start start', 'end end']`. Convierte scrollYProgress en strokeDashoffset usando la circunferencia completa como valor inicial. Aplica la motion value directamente sobre un elemento motion.circle.

    const { scrollYProgress } = useScroll({
      target: sectionRef,
      offset: ["start start", "end end"],
    });
    const circumference = 2 * Math.PI * 54;
    const progressOffset = useTransform(
      scrollYProgress,
      [0, 1],
      [circumference, 0]
    );
    // In JSX:
    // <motion.circle style={{ strokeDashoffset: progressOffset }} ... />
  3. Mapear la fracción de scroll al paso activo

    useTransform puede mapear un array de valores de entrada a un array de valores de salida. Aliméntalo con fracciones espaciadas de forma uniforme (una por paso) y los índices de paso correspondientes. Cada tarjeta deriva entonces su estado activo con Math.round.

    const currentStepIndex = useTransform(
      scrollYProgress,
      steps.map((_, i) => i / steps.length),
      steps.map((_, i) => i),
    );
    // Per card:
    const isActive = useTransform(currentStepIndex, (v) => Math.round(v) === i);
  4. Animar las tarjetas de paso con motion values derivadas

    Pasa isActive (un MotionValue<boolean>) por nuevas llamadas a useTransform para obtener opacity, color de borde y color de fondo. Aplícalos como estilos motion inline en cada tarjeta, sin re-renders, todo gestionado en el hilo de animación.

    <motion.div
      style={{
        opacity: useTransform(isActive, (v) => (v ? 1 : 0.35)),
        borderLeftColor: useTransform(isActive, (v) =>
          v ? "var(--color-accent)" : "var(--color-border)"
        ),
      }}
    >

Cuándo usarlo

Recurre a este patrón en las landing pages de producto que necesitan explicar un proceso de varios pasos sin muros de texto, flujos de onboarding, explicaciones de funcionalidades SaaS, guías de instalación. Funciona mejor con 3 a 5 pasos; menos pasos desaprovechan el espacio de scroll, más pasos hacen el contenedor incómodamente alto en móvil. Evítalo en las páginas centradas en la conversión donde importa la fricción del scroll hacia el CTA, y en las páginas donde los usuarios llegan a mitad del scroll desde enlaces de ancla (el anillo de progreso quedará en un estado inesperado).

Usado por

  • Stripe, Usa secciones sticky con progresión paso a paso para explicar los flujos de pago y el onboarding de desarrolladores.
  • Notion, Emplea secuencias de pasos bloqueadas al scroll en sus páginas de producto para guiar la configuración de un espacio de trabajo.
  • Linear, Recorridos de funcionalidades guiados por el scroll que resaltan cada capacidad a medida que el usuario avanza por la sección de marketing.
  • Lottie by LottieFiles, Animaciones controladas por el scroll ligadas al progreso de la página para demostrar paso a paso cómo funciona su formato de animación.

FAQ

¿Por qué la altura de la sección se fija en steps.length × 100vh?

Cada paso necesita un rango de scroll equivalente para estar en primer plano. Con 100vh por paso, desplazarse una altura de viewport transiciona de un paso al siguiente. Usa menos vh por paso para un ritmo más rápido, más para una lectura más lenta.

¿Puedo reemplazar el anillo circular por una barra de progreso lineal?

Sí. Reemplaza el SVG por un div, vincula su width o scaleX a la misma motion value scrollYProgress mediante useTransform, y el resto de la lógica permanece idéntica.

¿Funciona en móvil?

La interacción de scroll funciona en todos los dispositivos, incluidas las pantallas táctiles, useScroll rastrea el scroll iniciado por toque igual que el scroll con ratón. La cuadrícula de dos columnas pasa a una sola columna mediante la clase responsive de Tailwind.

¿Cómo añado un vídeo real en lugar del anillo de progreso?

Mapea scrollYProgress a la propiedad currentTime de un elemento vídeo HTML mediante un useEffect que lee la motion value. El vídeo con scrubbing al scroll es una técnica aparte: necesitas un vídeo precargado y requestAnimationFrame para sincronizar el cabezal de reproducción sin provocar layout thrash.

"use client";

import { useRef } from "react";
import { motion, useScroll, useTransform } from "framer-motion";

interface ScrollStep {
  title: string;
  description: string;
}

interface VideoScrollPlayProps {
  title?: string;
  subtitle?: string;
  steps?: ScrollStep[];
}

const EASE = [0.16, 1, 0.3, 1] as const;

export default function VideoScrollPlay({
  title = "Comment ca fonctionne",
  subtitle = "Scrollez pour decouvrir",
  steps = [],

Code complet réservé à Pro

Code source intégral, export multi-framework et playground.

Passer en Pro, 9,99€/mois

Reseñas

Sección React con progreso al hacer scroll, Tutorial Framer