Scroll Zoom Reveal
Um efeito de scroll pronto para uso, sem dependências:
- Cena fixa em tela cheia: um título sobre uma cena escurecida que preenche a viewport.
- Movimento de câmera + afastamento: a cena se estabiliza levemente, então a 'câmera' se afasta. A cena revela estar em um monitor de laptop (a moldura e a base aparecem).
- Revelação do aplicativo: painéis do editor (barra de ferramentas, organizador, propriedades, timeline) deslizam e a cena se encolhe dentro do painel da viewport.
- Texto com fade-in abaixo (
data-zr-fade), depois uma galeria com abas e crossfade com um seletor deslizante.
Todos os arquivos prontos estão em assets/ ao lado deste arquivo:
| Arquivo | Finalidade |
|---|---|
assets/zoom-reveal.css | Todos os estilos (tokens de tema em .zr) |
assets/zoom-reveal.js | Componente + fades + galeria. Expõe window.ZoomReveal, inicializa automaticamente [data-zoom-reveal] / [data-zr-gallery] / [data-zr-fade] |
assets/oled-field.js | Opcional. Campo de subpixels OLED reativo ao cursor (window.OledField, inicializa automaticamente [data-zr-oled]). Os estilos estão em zoom-reveal.css |
assets/placeholder-scenes.js | Opcional. Pinta proceduralmente arte de placeholder original no canvas (ZRScenes.drawCity, ZRScenes.drawCanyon) até que imagens reais existam |
assets/demo.html | Página funcional completa. Comece por esta marcação. |
Como usar em um site
- Copie os assets para o projeto (ex.:
public/zoom-reveal/ousrc/vendor/zoom-reveal/). Copieplaceholder-scenes.jsapenas se ainda não houver artwork real. - Leia
assets/demo.htmle copie o bloco<section class="zr" data-zoom-reveal>…</section>(mais o bloco da galeria, se desejado). Mantenha a estrutura de classes exatamente:
section.zr[data-zoom-reveal] ← o comprimento do scroll vive aqui (--zr-length)
.zr-sticky ← palco fixo de 100vh
.zr-glow (brilho ambiente opcional)
.zr-device ← redimensionado pelo JS
.zr-lid > .zr-display
.zr-viewport > .zr-media > img | video | canvas ← SUA CENA
.zr-chrome > .zr-panel.zr-toolbar / .zr-left / .zr-right / .zr-bottom (UI do app opcional)
.zr-base (base do laptop; remova para monitor/tablet)
.zr-scrim + header.zr-intro ← título sobre a cena, desvanece primeiro
.zr-caption ← legenda pequena, aparece no final
- Carregue
zoom-reveal.cssno<head>ezoom-reveal.jsno final do<body>. - Troque a mídia: substitua o canvas de demonstração pela cena real:
- Imagem:
<img src="cena.avif" alt="" decoding="async">. Exporte com pelo menos o tamanho da viewport × DPR (2560–3200px de largura). O efeito começa com a imagem preenchendo toda a tela. - Vídeo com scrub no scroll:
<video data-zr-scrub muted playsinline preload="auto" src="cena.mp4"></video>. O JS mapeia o progresso do scroll paracurrentTime. Codifique com cada quadro como keyframe para que a busca seja suave:ffmpeg -i in.mov -c:v libx264 -g 1 -crf 22 -an -movflags +faststart cena.mp4.
- Imagem:
- Reescreva todo o texto (eyebrow, título, legenda, rótulos dos painéis, nome do arquivo, rótulos da galeria) para se adequar ao site. Nunca publique o texto fictício "Orbit / Forge" da demonstração.
- Tema com os tokens em
.zr:--zr-bg,--zr-fg,--zr-muted,--zr-accent,--zr-length(distância do scroll, padrão360vh).
Opções
Passe JSON no atributo, ex.: data-zoom-reveal='{"maxWidth":1000,"viewport":{"top":0,"right":0,"bottom":0,"left":0}}', ou construa em JS: new ZoomReveal(el, options).
| Opção | Padrão | Significado |
|---|---|---|
maxWidth | 1180 | Largura máxima final da tela (px) |
widthRatio / heightRatio | 0.82 / 0.64 | Tamanho final da tela em relação à viewport |
aspect | 0.625 | Altura ÷ largura da tela (16:10). Telefone ≈ 2.16, ultrawide ≈ 0.42 |
overscan | 1.04 | Quão além da viewport a tela começa (esconde a moldura) |
dolly | 1.14 | Zoom interno inicial da mídia; estabiliza em 1 |
lift | 0.035 | Quanto o dispositivo sobe no final |
timeline | veja JS | Janelas de progresso [início,fim] para intro, dolly, zoom, chrome, caption |
viewport | {top:6.5,right:21,bottom:22,left:15} | Insetos finais (% da tela) do painel da cena dentro da UI do app. Defina todos como 0 e remova .zr-chrome para um puro "cena → tela do dispositivo" sem UI do app |
onProgress | — | fn(p) callback com progresso 0..1 |
Eventos: a seção dispara zr:resize com {width, height, finalWidth}, o tamanho em pixels no qual a mídia é disposta. Use para dimensionar canvas ou escolher uma fonte de imagem.
React / Next.js
Importe o CSS globalmente. Remova data-zoom-reveal da marcação e construa manualmente para vinculá-lo ao ciclo de vida do componente ('use client' no 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();
}, []);
Converta class → className e style="--v:78%" → style={{'--v':'78%'}}.
Campo de pixels OLED (efeito de cursor)
Um fundo em tela cheia de minúsculas barras de subpixels RGB, levemente iluminadas. Mover o cursor agita uma pequena simulação de fluido. Os pixels ao longo do caminho acendem, crescem e mudam de cor de violeta para ciano para lima para branco quente, então o brilho deriva na direção do cursor, se espalha e desvanece. Um clique ou toque envia uma ondulação. Quando o cursor está ocioso (e em telas sensíveis ao toque), um "cursor fantasma" lento o mantém ativo. A renderização é um shader de fragmento WebGL1, a simulação é uma pequena grade de CPU, e só é executada enquanto a seção está na tela. Sem WebGL, cai para um spotlight CSS.
Use atrás de um bloco de especificações/estatísticas, um hero ou uma faixa CTA. Qualquer elemento funciona: adicione class="zr-oled" data-zr-oled, coloque conteúdo dentro, carregue zoom-reveal.css + oled-field.js. O componente insere o canvas automaticamente. Veja o bloco "OLED PIXEL FIELD" em assets/demo.html.
<section class="zr-oled" data-zr-oled='{"cell":7}'>
<div class="seu-conteudo">…estatísticas / título…</div>
</section>
<script src="oled-field.js"></script>
| Opção | Padrão | Significado |
|---|---|---|
cell | 6 | px CSS por pixel (3 barras de subpixels). 5–9 parece bom; use maior no mobile para desempenho |
radius | 2.4 | Raio do pincel em células |
strength | 0.55 | Quanta luz o cursor adiciona. Os padrões são deliberadamente sutis; 1 é ousado |
decay | 0.94 | Desvanecimento por quadro; maior = rastros mais longos (0.92 curto … 0.97 longo) |
flow | 0.55 | Quão longe o brilho deriva ao longo da direção do cursor |
diffusion | 0.35 | Quão rápido o brilho se espalha |
base | 0.09 | Brilho da grade não iluminada |
palette | ['#5a3cff','#1fd6ff','#b6ff4a','#fff4d8'] | 4 cores, baixa → alta energia. Combine com a marca, ex.: quente ['#ff3c5a','#ff9a1f','#ffe14a','#fff8e8'] |
ambient / ripples | true | Cursor fantasma enquanto ocioso / ondulação ao clicar |
Para um visual mais ousado (mais próximo de um efeito de destaque), use {"radius":3.2,"strength":1,"decay":0.962,"flow":0.9}. Mantenha os padrões sutis atrás de seções com muito texto.
API: const f = new OledField(el, opts), f.pulse(clientX, clientY) (dispara uma ondulação a partir do código), f.destroy() (chame na limpeza do React). Dê ao texto sobre o campo uma text-shadow escura suave para que permaneça legível sobre rastros brilhantes.
Armadilhas (verifique estas antes de finalizar)
- Sticky quebra se qualquer ancestral de
.zrtiveroverflow: hidden/auto/clipou umtransform. Useoverflow-x: clipnobodyse o overflow horizontal precisar ser ocultado. - Não envolva
.zrem bibliotecas de scroll suave que transformam a página (Locomotive v4 etc.). Lenis está ok. - O dispositivo é disposto grande e redimensionado para baixo, então a mídia permanece nítida. Não coloque tamanhos fixos em px dentro de
.zr-device: useem(1em = 1% da largura do display). - Movimento reduzido é tratado: a seção desafixa e mostra o estado final; a galeria para de reproduzir automaticamente; o campo OLED continua reagindo ao cursor, mas remove o cursor fantasma e as ondulações.
- Animações pausam em abas em segundo plano (requestAnimationFrame para). Ao testar o campo OLED em um painel do navegador, mantenha a página visível ou parecerá congelada.
- Abaixo de ~500px de largura, o laptop é pequeno. Considere
{"widthRatio":0.92}ouaspectcorrespondente a um quadro de telefone no mobile. - Teste rolando lentamente de cima para baixo no navegador nas larguras desktop e mobile. Confirme que o título desvanece antes do afastamento começar, nenhuma moldura aparece no início, e a legenda não sobrepõe a base.
- Use artwork e texto originais ou licenciados. Isso recria a técnica de movimento; não copie imagens, renders de produto ou texto de outra marca.
