Hero · Hero
Scroll Zoom Hero
Imagem inicia pequena e ocupa a viewport com scroll.
Hero sticky com cinco imagens: a 01 domina, a 02 entra com parallax, a 03 aumenta, a 04 cruza lateralmente e a 05 assume o background, com textos sincronizados.
Preview executado em iframe sandbox isolado. Efeitos de scroll: role dentro do preview. Em touch, interações de mouse viram animação automática.
Transforme o efeito personalizado em uma instrução técnica clara para sua IA.
Stack
import { useEffect, useRef } from "react";
// Five Images Scroll Hero — gerado pelo Effect Prompt Lab
// Isolado em Shadow DOM para não vazar CSS. Cleanup automático no unmount.
const SETTINGS = {
"primary": "#8b5cf6",
"secondary": "#22d3ee",
"background": "#0b0b12",
"text": "#f5f5f7",
"speed": 1,
"duration": 2,
"delay": 0,
"intensity": 60,
"opacity": 100,
"blur": 10,
"glow": 24,
"scale": 1,
"rotation": 0,
"depth": 120,
"perspective": 900,
"count": 40,
"direction": "right",
"loop": true,
"autoplay": true,
"mouse": true,
"scroll": true,
"mobile": true,
"reducedMotion": true,
"start": 0,
"end": 100,
"prefix": "",
"suffix": "",
"separator": ".",
"images": [
"aurora",
"city",
"ocean",
"forest",
"desert"
],
"headline": "Sua próxima página começa aqui",
"transition": "blur",
"zoom": 1.25,
"parallax": 40,
"scrub": 1
};
const CSS = ":host{--fx-primary:#8b5cf6;--fx-secondary:#22d3ee;--fx-bg:#0b0b12;--fx-text:#f5f5f7;--fx-speed:1;--fx-dur:2.00s;--fx-delay:0s;--fx-intensity:0.6;--fx-opacity:1;--fx-blur:10px;--fx-glow:24px;--fx-scale:1;--fx-rot:0deg;--fx-depth:120px;--fx-persp:900px;--fx-dir-x:1;--fx-dir-y:0;--fx-iter:infinite;--fx-play:running}\n.fh{height:520vh;position:relative}.st{position:sticky;top:0;height:100vh;overflow:hidden;perspective:var(--fx-persp)}.imgs,.im{position:absolute;inset:0}.im img{width:100%;height:100%;object-fit:cover}.im{will-change:transform,opacity,clip-path}.tx{position:absolute;inset:0;display:grid;place-items:center;pointer-events:none;z-index:20}.tx h2{grid-area:1/1;font:800 clamp(28px,6vw,72px)/1 system-ui;letter-spacing:-.03em;color:#fff;text-shadow:0 4px 40px rgba(0,0,0,.5);opacity:0;transition:opacity .5s,filter .5s,transform .5s;filter:blur(10px);transform:translateY(20px);text-align:center;padding:0 20px}.tx h2.on{opacity:1;filter:none;transform:none}.pg{position:absolute;left:24px;right:24px;bottom:24px;height:3px;background:rgba(255,255,255,.2);z-index:21;border-radius:3px}.pg i{display:block;height:100%;width:0;background:linear-gradient(90deg,var(--fx-primary),var(--fx-secondary));border-radius:3px}.after{min-height:60vh;display:grid;place-items:center;padding:40px;text-align:center}.after h3{font:600 20px system-ui;opacity:.7}.tt-fade .tx h2{filter:none;transform:none}.tt-slide .tx h2{filter:none;transform:translateX(80px)}.tt-zoom .tx h2{filter:none;transform:scale(.7)}body .tx h2.on{opacity:1;filter:none;transform:none}.im::after{content:'';position:absolute;inset:0;background:linear-gradient(to top,rgba(0,0,0,.45),transparent 50%)}.seq .im1{inset:14vh 6vw 14vh auto;width:40vw;border-radius:20px;overflow:hidden;box-shadow:0 30px 80px rgba(0,0,0,.5)}.seq .im2{inset:22vh 30vw;border-radius:24px;overflow:hidden}.seq .im3{inset:18vh auto 18vh 8vw;width:36vw;border-radius:20px;overflow:hidden}.seq .im4{z-index:10}";
const HTML = "<section class=\"fh seq\"><div class=\"st\"><div class=\"imgs\"><div class=\"im im0\"><img alt=\"Imagem 1\" data-i=\"0\"></div><div class=\"im im1\"><img alt=\"Imagem 2\" data-i=\"1\"></div><div class=\"im im2\"><img alt=\"Imagem 3\" data-i=\"2\"></div><div class=\"im im3\"><img alt=\"Imagem 4\" data-i=\"3\"></div><div class=\"im im4\"><img alt=\"Imagem 5\" data-i=\"4\"></div></div><div class=\"tx\"><h2 class=\"t0\">Descubra o novo</h2><h2 class=\"t1\">Entre na experiência</h2><h2 class=\"t2\">Aproxime-se dos detalhes</h2><h2 class=\"t3\">Atravesse fronteiras</h2><h2 class=\"t4\">Chegue ao destino</h2></div><div class=\"pg\"><i></i></div></div></section><section class=\"after\"><h3>Fim da sequência — conteúdo continua normalmente</h3></section><div class=\"fx-hint\">Role dentro do preview ↓</div>";
const RUNTIME = "(function(){\nvar FX=window.FX;\nFX.reduced=!!FX.reducedMotion&&window.matchMedia('(prefers-reduced-motion: reduce)').matches;\nFX.isTouch=window.matchMedia('(hover: none)').matches;\nif(FX.reduced)document.documentElement.classList.add('fx-reduced');\nwindow.$=function(s,r){return (r||document).querySelector(s)};\nwindow.$$=function(s,r){return Array.prototype.slice.call((r||document).querySelectorAll(s))};\nFX.clamp=function(v,a,b){return Math.min(b,Math.max(a,v))};\nFX.lerp=function(a,b,t){return a+(b-a)*t};\nFX.tick=function(fn){var id,t0=performance.now(),run=true;function f(t){if(run&&!document.hidden)fn((t-t0)/1000*FX.speed,t);if(run)id=requestAnimationFrame(f)}id=requestAnimationFrame(f);return{stop:function(){run=false;cancelAnimationFrame(id)}}};\nFX.pointer=function(el,cb){el=el||document.body;var last=-1e9;function mv(e){var p=e.touches?e.touches[0]:e;var r=el.getBoundingClientRect();last=performance.now();cb((p.clientX-r.left)/r.width,(p.clientY-r.top)/r.height,true,p.clientX-r.left,p.clientY-r.top)}\nif(FX.mouse){el.addEventListener('mousemove',mv);el.addEventListener('touchmove',mv,{passive:true});el.addEventListener('touchstart',mv,{passive:true});}\nif(FX.autoplay&&!FX.reduced)FX.tick(function(t){if(performance.now()-last>2500){var x=.5+Math.sin(t*.9)*.3,y=.5+Math.cos(t*1.3)*.25;var r=el.getBoundingClientRect();cb(x,y,false,x*r.width,y*r.height)}})};\nFX.progress=function(el){var r=el.getBoundingClientRect();var d=r.height-innerHeight;return d<=0?0:FX.clamp(-r.top/d,0,1)};\nFX.visible=function(el){var r=el.getBoundingClientRect();return FX.clamp((innerHeight-r.top)/(innerHeight+r.height),0,1)};\nFX.onScroll=function(cb){var q=false;function h(){if(q)return;q=true;requestAnimationFrame(function(){q=false;cb(scrollY)})}if(FX.scroll)addEventListener('scroll',h,{passive:true});cb(scrollY)};\nFX.inView=function(els,cb,th){var io=new IntersectionObserver(function(es){es.forEach(function(e){cb(e.target,e.isIntersecting,e)})},{threshold:th||.2});els.forEach(function(e){io.observe(e)});return io};\nFX.img=function(i,w,h){var s=(FX.images&&FX.images[i%5])||('fx'+i);if(/^(https?:|data:|\\/)/.test(s))return s;return 'https://picsum.photos/seed/'+encodeURIComponent(s+(i>=5?i:''))+'/'+(w||800)+'/'+(h||600)};\nFX.canvas=function(el){var c=document.createElement('canvas');c.style.cssText='position:absolute;inset:0;width:100%;height:100%;display:block';el.appendChild(c);var ctx=c.getContext('2d');var o={c:c,ctx:ctx,w:0,h:0};function rs(){var d=Math.min(2,devicePixelRatio||1);o.w=el.clientWidth;o.h=el.clientHeight;c.width=o.w*d;c.height=o.h*d;ctx.setTransform(d,0,0,d,0,0)}rs();addEventListener('resize',rs);return o};\nFX.hex=function(h,a){h=h.replace('#','');if(h.length===3)h=h.split('').map(function(x){return x+x}).join('');var n=parseInt(h,16);return 'rgba('+(n>>16&255)+','+(n>>8&255)+','+(n&255)+','+(a==null?1:a)+')'};\nwindow.$$=window.$$;document.querySelectorAll('[data-fx-headline]').forEach(function(e){if(FX.headline)e.textContent=FX.headline});\nFX.fmt=function(v,dec){var s=Math.round(v).toString();if(FX.separator)s=s.replace(/\\B(?=(\\d{3})+(?!\\d))/g,FX.separator);return (FX.prefix||'')+s+(FX.suffix||'')};\n})();";
const EFFECT = "document.body.classList.add('tt-'+FX.transition);$('.fh').style.height=(300+FX.duration*110)+'vh';$$('.im img').forEach(function(im){im.src=FX.img(+im.dataset.i,1600,1000)});var sec=$('.fh'),ims=$$('.im'),ts=$$('.tx h2'),bar=$('.pg i'),cur=0,tgt=0,dir=FX.direction==='left'?-1:1;function k(v,a){return FX.clamp(v-a,0,1)}function e(v){return v<.5?2*v*v:1-Math.pow(-2*v+2,2)/2}FX.onScroll(function(){tgt=FX.progress(sec)});FX.tick(function(){var s=FX.scrub>0?Math.min(1,.016/(FX.scrub*.12)):1;cur=FX.lerp(cur,tgt,s);var p=cur,seg=p*4,bl=FX.blur,Z=FX.zoom,par=FX.parallax/100;bar.style.width=p*100+'%';var ti=Math.min(4,Math.floor(p*5));ts.forEach(function(t,i){t.classList.toggle('on',i===ti)});render(p,seg,bl,Z,par)});function render(p,seg,bl,Z,par){ims[0].style.transform='scale('+(1+p*(Z-1))+')';var a=e(k(seg,0));ims[1].style.transform='translateY('+((1-a)*110+(-p*par*40))+'vh)';ims[1].style.filter='blur('+(1-a)*bl+'px)';var b=e(k(seg,1));ims[2].style.transform='scale('+(.15+b*.85)+')';ims[2].style.opacity=b>0?1:0;var c=k(seg,2);ims[3].style.transform='translateX('+(dir*(c<1?(-120+c*120):0))+'vw)';ims[3].style.opacity=c>0?1:0;var d=e(k(seg,3));ims[4].style.clipPath='circle('+(d*110)+'% at 50% 50%)';ims[4].style.transform='scale('+(Z-(Z-1)*d)+')';ims[4].style.filter='blur('+(1-d)*bl*.5+'px)'}";
export default function FiveImagesScrollHero() {
const hostRef = useRef<HTMLDivElement>(null);
useEffect(() => {
const host = hostRef.current;
if (!host) return;
// Para produção, prefira portar o EFFECT para hooks nativos (useEffect + refs).
const iframe = document.createElement("iframe");
iframe.title = "Five Images Scroll Hero";
iframe.setAttribute("sandbox", "allow-scripts");
iframe.style.cssText = "width:100%;height:100%;border:0;display:block";
iframe.srcdoc =
"<!doctype html><html><head><style>" + CSS.replace(":host", ":root") + "</style></head><body>" + HTML +
"<script>window.FX=" + JSON.stringify(SETTINGS) + ";" + RUNTIME + ";(function(){" + EFFECT + "})()<\/script></body></html>";
host.appendChild(iframe);
return () => {
iframe.remove(); // destrói RAF, listeners e observers do efeito
};
}, []);
return <div ref={hostRef} className="relative h-[480px] w-full overflow-hidden rounded-2xl" aria-label="Five Images Scroll Hero" />;
}
npm i gsap @gsap/reactincluso no pacote gsapO preview usa JS nativo equivalente. Em produção, use GSAP, ScrollTrigger somente se o projeto justificar.
Sequência sticky com troca de imagens sincronizada ao texto.
Hero · Hero
Imagem inicia pequena e ocupa a viewport com scroll.
Hero · Cinco imagens
Cinco cenas viajam horizontalmente durante scroll vertical.