¿Qué hace Scroll Zoom Reveal?
Un efecto de desplazamiento listo para usar, sin dependencias:
- Escena anclada a pantalla completa: un titular sobre una escena oscurecida que llena el viewport.
- Dolly + zoom-out: la escena se asienta ligeramente, luego la "cámara" se aleja. La escena resulta estar en la pantalla de una laptop (el bisel y la base aparecen).
- Revelado de la aplicación: paneles del editor (barra de herramientas, organizador, propiedades, línea de tiempo) se deslizan y la escena se encoge dentro de su panel de viewport.
- Texto con fade-in debajo (
data-zr-fade), luego una galería de pestañas con crossfade y un indicador deslizante tipo píldora.
Todos los archivos listos para usar están en assets/ junto a este archivo:
| Archivo | Propósito |
|---|---|
assets/zoom-reveal.css | Todos los estilos (tokens de tema en .zr) |
assets/zoom-reveal.js | Componente + fades + galería. Expone window.ZoomReveal, auto-inicia [data-zoom-reveal] / [data-zr-gallery] / [data-zr-fade] |
assets/oled-field.js | Opcional. Campo OLED de subpíxeles reactivo al cursor (window.OledField, auto-inicia [data-zr-oled]). Los estilos están en zoom-reveal.css |
assets/placeholder-scenes.js | Opcional. Pinta proceduralmente arte de placeholder original en canvas (ZRScenes.drawCity, ZRScenes.drawCanyon) hasta que existan imágenes reales |
assets/demo.html | Página completa funcional. Empieza desde este marcado. |
Cómo usarlo en un sitio
- Copia los assets en el proyecto (ej.
public/zoom-reveal/osrc/vendor/zoom-reveal/). Copiaplaceholder-scenes.jssolo si aún no hay arte real. - Lee
assets/demo.htmly copia el bloque<section class="zr" data-zoom-reveal>…</section>(más el bloque de galería si se desea). Mantén la estructura de clases exactamente:
section.zr[data-zoom-reveal] ← aquí vive la longitud de scroll (--zr-length)
.zr-sticky ← escenario anclado de 100vh
.zr-glow (brillo ambiental opcional)
.zr-device ← escalado por JS
.zr-lid > .zr-display
.zr-viewport > .zr-media > img | video | canvas ← TU ESCENA
.zr-chrome > .zr-panel.zr-toolbar / .zr-left / .zr-right / .zr-bottom (interfaz de aplicación opcional)
.zr-base (base de laptop; eliminar para monitor/tableta)
.zr-scrim + header.zr-intro ← titular sobre la escena, se desvanece primero
.zr-caption ← leyenda pequeña, aparece al final
- Carga
zoom-reveal.cssen<head>yzoom-reveal.jsal final de<body>. - Cambia el contenido multimedia: reemplaza el canvas de demostración por la escena real:
- Imagen:
<img src="scene.avif" alt="" decoding="async">. Expórtala al menos al tamaño del viewport × DPR (2560–3200px de ancho). El efecto comienza con la imagen llenando toda la pantalla. - Video sincronizado con scroll:
<video data-zr-scrub muted playsinline preload="auto" src="scene.mp4"></video>. JS mapea el progreso del scroll acurrentTime. Codifica con cada cuadro como keyframe para que la búsqueda sea suave:ffmpeg -i in.mov -c:v libx264 -g 1 -crf 22 -an -movflags +faststart scene.mp4.
- Imagen:
- Reescribe todo el texto (ceja, titular, leyenda, etiquetas de panel, nombre de archivo, etiquetas de galería) para que se ajusten al sitio. Nunca envíes el texto ficticio "Orbit / Forge" de la demo.
- Personaliza con los tokens en
.zr:--zr-bg,--zr-fg,--zr-muted,--zr-accent,--zr-length(distancia de scroll, por defecto360vh).
Opciones
Pasa JSON en el atributo, ej. data-zoom-reveal='{"maxWidth":1000,"viewport":{"top":0,"right":0,"bottom":0,"left":0}}', o constrúyelo en JS: new ZoomReveal(el, options).
| Opción | Por defecto | Significado |
|---|---|---|
maxWidth | 1180 | Ancho máximo final de la pantalla (px) |
widthRatio / heightRatio | 0.82 / 0.64 | Tamaño final de la pantalla en relación al viewport |
aspect | 0.625 | Alto ÷ ancho de la pantalla (16:10). Teléfono ≈ 2.16, ultraancho ≈ 0.42 |
overscan | 1.04 | Cuánto más allá del viewport comienza la pantalla (oculta el bisel) |
dolly | 1.14 | Zoom interno inicial del contenido multimedia; se asienta a 1 |
lift | 0.035 | Cuánto se eleva el dispositivo al final |
timeline | ver JS | Ventanas de progreso [inicio,fin] para intro, dolly, zoom, chrome, caption |
viewport | {top:6.5,right:21,bottom:22,left:15} | Insets finales (% de la pantalla) del panel de escena dentro de la interfaz de la app. Pon todos a 0 y elimina .zr-chrome para un zoom-out puro de "escena → pantalla de dispositivo" sin interfaz de app |
onProgress | — | fn(p) callback con progreso 0..1 |
Eventos: la sección despacha zr:resize con {width, height, finalWidth}, el tamaño en píxeles con el que se muestra el contenido multimedia. Úsalo para dimensionar canvases o elegir una fuente de imagen.
React / Next.js
Importa el CSS globalmente. Elimina data-zoom-reveal del marcado y constrúyelo manualmente para que esté vinculado al ciclo de vida del componente ('use client' en Next):
useEffect(() => {
import('./zoom-reveal/zoom-reveal.js').then(() => { // registra window.ZoomReveal
const zr = new window.ZoomReveal(ref.current, { maxWidth: 1100 });
const gal = galRef.current && new window.ZoomReveal.Gallery(galRef.current);
window.ZoomReveal.initFades();
cleanup = () => zr.destroy();
});
let cleanup = () => {};
return () => cleanup();
}, []);
Convierte class → className y style="--v:78%" → style={{'--v':'78%'}}.
Campo OLED de píxeles (efecto hover del cursor)
Un fondo a pantalla completa de pequeñas barras de subpíxeles RGB, tenuemente iluminadas. Mover el cursor agita una pequeña simulación de fluido. Los píxeles en la trayectoria se iluminan, crecen y cambian de color de violeta a cian a lima a blanco cálido, luego el resplandor se desplaza en la dirección del cursor, se extiende y se desvanece. Un clic o toque envía una onda expansiva. Cuando el cursor está inactivo (y en pantallas táctiles), un "cursor fantasma" lento lo mantiene vivo. El renderizado es un fragment shader WebGL1, la simulación es una pequeña rejilla en CPU, y solo se ejecuta mientras la sección está en pantalla. Sin WebGL, cae en un spotlight CSS.
Úsalo detrás de un bloque de especificaciones/estadísticas, un héroe o una banda CTA. Cualquier elemento funciona: añade class="zr-oled" data-zr-oled, pon contenido dentro, carga zoom-reveal.css + oled-field.js. El componente inserta el canvas por sí mismo. Consulta el bloque "OLED PIXEL FIELD" en assets/demo.html.
<section class="zr-oled" data-zr-oled='{"cell":7}'>
<div class="your-content">…estadísticas / titular…</div>
</section>
<script src="oled-field.js"></script>
| Opción | Por defecto | Significado |
|---|---|---|
cell | 6 | Píxeles CSS por píxel (3 barras de subpíxeles). 5–9 se ve bien; usa más grande en móvil para rendimiento |
radius | 2.4 | Radio del pincel en celdas |
strength | 0.55 | Cuánta luz añade el cursor. Los valores por defecto son deliberadamente sutiles; 1 es audaz |
decay | 0.94 | Desvanecimiento por fotograma; más alto = estelas más largas (0.92 corto … 0.97 largo) |
flow | 0.55 | Cuánto se desplaza el resplandor a lo largo de la dirección del cursor |
diffusion | 0.35 | Qué tan rápido se extiende el resplandor |
base | 0.09 | Brillo de la cuadrícula sin iluminar |
palette | ['#5a3cff','#1fd6ff','#b6ff4a','#fff4d8'] | 4 colores, baja → alta energía. Combina con la marca, ej. cálido ['#ff3c5a','#ff9a1f','#ffe14a','#fff8e8'] |
ambient / ripples | true | Cursor fantasma mientras inactivo / onda al hacer clic |
Para un aspecto más audaz (más cercano a un efecto de exhibición) usa {"radius":3.2,"strength":1,"decay":0.962,"flow":0.9}. Mantén los valores por defecto sutiles detrás de secciones con mucho texto.
API: const f = new OledField(el, opts), f.pulse(clientX, clientY) (disparar una onda desde código), f.destroy() (llamar en la limpieza de React). Dale al texto sobre el campo una suave text-shadow oscura para que siga siendo legible sobre las estelas brillantes.
Errores comunes (verifica antes de terminar)
- El sticky se rompe si algún ancestro de
.zrtieneoverflow: hidden/auto/clipo untransform. Usaoverflow-x: clipenbodysi el desbordamiento horizontal debe ocultarse. - No envuelvas
.zren librerías de scroll suave que transformen la página (Locomotive v4 etc.). Lenis está bien. - El dispositivo se coloca grande y se escala hacia abajo, por lo que el contenido multimedia permanece nítido. No pongas tamaños fijos en px dentro de
.zr-device: usaem(1em = 1% del ancho de la pantalla). - Movimiento reducido está manejado: la sección se desancla y muestra el estado final; la galería deja de reproducirse automáticamente; el campo OLED sigue reaccionando al cursor pero elimina el cursor fantasma y las ondas.
- Las animaciones se pausan en pestañas en segundo plano (requestAnimationFrame se detiene). Al probar el campo OLED en un panel del navegador, mantén la página visible o parecerá congelada.
- Por debajo de ~500px de ancho la laptop es pequeña. Considera
{"widthRatio":0.92}oaspectajustado a un marco de teléfono en móvil. - Prueba desplazándote lentamente de arriba a abajo en el navegador en anchos de escritorio y móvil. Confirma que el titular se desvanezca antes de que comience el zoom-out, que no aparezca bisel al inicio y que la leyenda no se superponga a la base.
- Usa arte y texto originales o con licencia. Esto recrea la técnica de movimiento; no copies imágenes, renders de productos o texto de otra marca.
