Luca Perullo
Components

Component

LiveNew

Scroll Video

La tecnica dell'iPhone product page: una sezione tall (250vh+) con un canvas pinned sticky-top. Mentre l'utente scrolla, la progress dello scroll mappa l'indice del frame da disegnare. Frames WebP precaricati in batch (eager + deferred via requestIdleCallback), drawImage GPU-friendly, fallback completo per prefers-reduced-motion (mostra solo il primo frame, niente pin).

PremiumScrollClient
Open in ClaudeSorgente

Esempio01

scroll-video.tsx
Frame 042 · 150Scroll-driven sequence
0%100%

Note Asset prep con ffmpeg (esempio per 150 frames a 1280x720 da un mp4): ffmpeg -i source.mp4 -vf 'fps=30,scale=1280:-2' -frames:v 150 -c:v libwebp -q:v 80 -loop 1 -an public/scroll-video/frame-%04d.webp Linee guida peso: 1280x720 WebP q=80 ≈ 30-80 KB/frame → 150 frames ≈ 5-12 MB totali. Sotto 10 MB è sicuro su 4G; sopra, considera frame stride (1 frame ogni 2 di scroll) o riduci la risoluzione. Eager batch default 12 frames per readiness rapida; il resto in requestIdleCallback con timeout 1500ms così non blocca il main thread durante l'idratazione.

Prompt LLM02

Incolla in Claude o ChatGPT per generare la tua variante. Include il contesto del brand, i token e i vincoli del progetto.

Prompt · scroll-video
Open in Claude
Sei un senior frontend engineer. Stai lavorando su un sito Next.js 16 + React 19 + Tailwind v4 in italiano, look chanhdai-inspired: colonna stretta 672px, Geist Sans + Geist Mono, hairline 1px, divisori a stripe diagonale, palette zinc.

Token CSS disponibili: --bg, --bg-alt, --fg, --fg-muted, --fg-soft, --border, --border-strong, --accent. Usa SEMPRE queste variabili tramite le utility tailwind generate (bg-bg, text-fg-muted, border-border, ecc.). Helper "cn" da "@/lib/utils". Niente librerie UI extra: solo lucide-react e tailwind-merge.

Genera un client component <ScrollVideo>: image-sequence player scroll-driven, stile Apple product page.

Props:
- baseUrl: string — URL fino al numero del frame (es. "/scroll-video/frame-")
- frameCount: number
- framePadding?: number (default 4) — zero-pad dell'indice (0001..0150)
- extension?: string (default "webp")
- scrollHeight?: string (default "250vh") — quanto è tall la sezione (più scroll = scrub più lento)
- aspectRatio?: string (default "16 / 9")
- maxWidth?: string (default "1200px")
- eagerFrameCount?: number (default 12) — frames caricati prioritariamente prima di mostrare
- loadingFallback?: ReactNode — content mentre carica
- showProgressBar?: boolean (default true) — barra di progress in basso
- className?, frameClassName?: string

Comportamento:
- "use client". Tutto via useEffect/useRef — niente state per ogni frame (sarebbe re-render storm).
- Preload eager batch parallelo con fetchpriority="high"; resto via requestIdleCallback con timeout 1500ms (fallback setTimeout 250ms).
- Refs: sectionRef, canvasRef, framesRef (HTMLImageElement[]), lastDrawnRef (numero ultimo frame disegnato).
- Scroll handler rAF-throttled. Scroll progress = clamp(-rect.top / max(1, sectionHeight - viewportHeight), 0, 1). targetIndex = floor(progress * frameCount).
- Se il targetIndex non è ancora caricato, walk indietro al frame loaded più vicino (no canvas vuoto).
- drawImage: aggiorna canvas.width/height alla naturalSize del frame solo al primo paint; CSS gestisce lo scaling.
- prefers-reduced-motion: niente sticky, niente scroll listener, niente progress bar — solo il primo frame statico.

Layout:
- <section> tall (scrollHeight) con sticky inner div top-0 h-screen flex items-center justify-center.
- Inner: <canvas> con aspect-ratio, maxWidth, transition-opacity per fade-in quando ready.
- Loading state: count "X / Y" mono, opzionale loadingFallback.
- Progress bar 1px in basso scaleX(eagerProgress) con transition-opacity → 0 a load completo.

Constraints: zero deps extra, transform-only sul layout, server-prerender safe (window guards in useEffect), accessibile (aria-hidden sul canvas).

Output: file completo .tsx (con "use client").

Uso tipico03

tsx
<ScrollVideo
  baseUrl="/scroll-video/frame-"
  frameCount={150}
  framePadding={4}
  extension="webp"
  scrollHeight="250vh"
  aspectRatio="16 / 9"
/>

Dipendenze04

npm
  • clsx
  • tailwind-merge
Interno
  • @/lib/utils#cn
  • WebP frames in /public/scroll-video/ (or any baseUrl)

Ti è servito? Dimmelo, oppure proponi il prossimo componente.