Aqueles sites em que o produto se desmonta no ar e se remonta sozinho. A ideia por trás é sempre a mesma: em vez do tempo, quem controla a animação é a posição da barra de rolagem. Role a página para ver as três técnicas funcionando.
position: sticky), você calcula o progresso da rolagem dentro dela (um número de 0 a 1) e usa esse número para posicionar cada elemento. Nada mais que isso.
Role para baixo: as camadas se separam, flutuam e caem de volta no prato.
Cada ingrediente é um elemento independente (aqui, um SVG). O JavaScript lê o quanto você já rolou dentro da seção presa e transforma isso num número de 0 a 1. Esse número vira a altura de cada camada, o giro e o quanto elas se afastam do centro.
// 1. progresso de 0 a 1 dentro do trilho da cena
const r = trilho.getBoundingClientRect();
const p = Math.min(1, Math.max(0, -r.top / (r.height - innerHeight)));
// 2. o progresso vira posição de cada camada
camadas.forEach((el, i) => {
const centro = (i - (n - 1) / 2); // -2.5, -1.5, ... 2.5
const abre = Math.sin(p * Math.PI); // abre no meio e fecha no fim
el.style.transform = `translate(-50%,-50%)
translateY(${centro * (28 + abre * 150)}px)
rotate(${abre * centro * 6}deg)`;
});
O Math.sin(p * Math.PI) é o truque que faz a peça abrir e fechar num movimento só: ele vale 0 no início, 1 no meio e 0 de novo no fim.
É a técnica da Apple: em vez de mover elementos, você desenha o quadro certo. Aqui o desenho é gerado na hora; num projeto real seriam imagens renderizadas em 3D.
Aqui não existem elementos se movendo: existe um <canvas> e, a cada rolagem, você desenha o quadro correspondente. Nos sites de produto famosos, esses quadros são imagens renderizadas em 3D (Blender, Cinema 4D) e exportadas como uma sequência de 120 a 240 arquivos.
// pré-carrega a sequência
const quadros = [];
for (let i = 0; i < TOTAL; i++) {
const img = new Image();
img.src = `frames/${String(i).padStart(4,'0')}.webp`;
quadros.push(img);
}
// desenha o quadro correspondente ao progresso
const i = Math.round(p * (TOTAL - 1));
ctx.drawImage(quadros[i], 0, 0, canvas.width, canvas.height);
O ponto de atenção é o peso. 200 quadros em WebP a 1600px dão facilmente 15 MB. Numa página de venda isso destrói a nota de performance. Na prática se usa 60 a 120 quadros, largura menor no celular e carregamento sob demanda.
Para efeitos mais simples (entrada, parallax, barra de progresso) já existe CSS nativo: animation-timeline: view(). Não precisa de biblioteca nem de JavaScript, e o navegador roda fora da thread principal, então não trava.
Seu navegador ainda não suporta animation-timeline: os cartões abaixo aparecem sem animação (é o fallback correto).
.card{
animation: entra linear both;
animation-timeline: view(); /* a linha do tempo é a rolagem */
animation-range: entry 12% cover 42%;
}
@keyframes entra{ to{ opacity:1; transform:none } }
| Técnica | Melhor para | Custo de produção | Risco |
|---|---|---|---|
| Camadas (SVG/PNG) | Produto que se abre em partes, "vista explodida", infográfico animado | Baixo: só precisa das peças recortadas | Pouco: é leve e todo navegador roda |
| Sequência de imagens | Produto real girando ou se montando com aparência fotográfica | Alto: exige modelagem e renderização 3D | Peso da página e tempo de produção |
| 3D no navegador (Three.js) | Quando o usuário precisa girar, trocar cor ou configurar o produto | Alto: modelo 3D + programação especializada | Celular fraco sofre; precisa de plano B |
| CSS nativo | Entradas, parallax, barra de progresso, revelação de seção | Muito baixo | Navegador antigo ignora (degrada bem) |
requestAnimationFrame.prefers-reduced-motion (tem gente que passa mal com movimento) e garanta que o conteúdo faça sentido sem a animação, porque é assim que o Google e o leitor de tela vão ler.