Um cabeçalho que diminui ao rolar é um padrão de interface comum em que um elemento de cabeçalho fixo na parte de cima da página faz uma transição suave para um tamanho menor conforme o usuário rola para baixo. Esse efeito é frequentemente usado para maximizar o espaço da tela para o conteúdo principal, mantendo os elementos essenciais de navegação ou elemento de marca acessíveis. Com as animações de rolagem do CSS, esse efeito pode ser alcançado de forma declarativa e eficiente, vinculando uma animação à posição de rolagem do documento.
Como fazer a implementação
Veja como criar um cabeçalho que diminui ao rolar a tela:
Crie um cabeçalho fixo: comece com um elemento de cabeçalho fixo na parte de cima da página e com uma altura predefinida.
<header>HEADER</header>header { position: fixed; height: 200px; top: 0; left: 0; right: 0; }Defina a animação de redução: crie uma animação CSS que mude a altura do cabeçalho.
@keyframes shrink { to { height: 50px; } }Aplicar a animação e a linha do tempo de rolagem: anexe a animação ao cabeçalho e use a função
scroll()para vinculá-la à posição de rolagem do documento.header { animation: shrink auto linear both; animation-timeline: scroll(block root); }Defina o
animation-range: use a propriedadeanimation-rangepara especificar a distância de rolagem em que a animação deve ocorrer. Por exemplo, para reduzir o cabeçalho nos primeiros 150 pixels de rolagem, useanimation-range: 0px 150px;.header { animation-range: 0px 150px; }
Dica: para evitar que o conteúdo após o cabeçalho seja ocultado por ele, adicione um padding-top ao body (ou ao contêiner de conteúdo principal) que seja igual à altura inicial do cabeçalho.
Dica: para garantir que o conteúdo da página role em sincronia com o cabeçalho que está diminuindo, defina o animation-range-end como a diferença entre os tamanhos inicial e final. Isso garante que a animação seja concluída exatamente quando o cabeçalho atingir o tamanho final. Nesta demonstração, o cabeçalho diminui de 200px para 50px, então o animation-range-end é definido como 150px.
Exemplo de código
@keyframes shrink {
to {
height: 50px;
}
}
header {
animation: shrink auto linear both;
animation-timeline: scroll(block root);
animation-range: 0px 150px;
}
Práticas recomendadas
Ao usar animações acionadas por rolagem, é importante seguir algumas práticas recomendadas para garantir uma experiência tranquila e acessível:
- FAÇA a detecção de recursos: nem todos os navegadores oferecem suporte a animações acionadas por rolagem. Use
@supports ((animation-timeline: scroll()) and (animation-range: 0% 100%))para verificar a compatibilidade e fornecer um substituto para navegadores que não oferecem suporte.- A verificação
(animation-range: 0% 100%)PRECISA ser incluída aqui para filtrar navegadores com suporte parcial. - NÃO use o pacote
scroll-timeline-polyfillpara a estratégia de substituição, porque ele não tem todos os recursos e tem muitos problemas conhecidos. - Se a animação for considerada apenas decorativa, opte pelo aprimoramento progressivo e NÃO forneça um substituto.
- A verificação
- RESPEITE as preferências do usuário: algumas pessoas preferem menos movimento na Web. Use a consulta de mídia
prefers-reduced-motionpara desativar ou reduzir as animações para esses usuários. - FAÇA o possível para animar apenas propriedades CSS de alto desempenho: para animações mais suaves, anime propriedades que podem ser processadas pela linha de execução do compositor do navegador, como
transformeopacity. Animar outras propriedades, comowidthouheight, pode causar problemas de desempenho. - USE a ordem de declaração correta: ao usar a propriedade abreviada
animation, declareanimation-timelineeanimation-rangedepois dela para evitar que a abreviação redefina a linha do tempo.
Ao usar a função scroll() para criar uma animação acionada por rolagem:
- OPCIONAL Seja explícito sobre o botão de rolagem: quando não estiver segmentando o botão de rolagem ancestral mais próximo, seja explícito sobre qual botão você quer usar com
scroll(root)ouscroll(self).- Quando
root,nearestouselfnão forem suficientes, use uma linha do tempo de rolagem nomeada.
- Quando
- OPCIONAL Seja explícito sobre o eixo a ser rastreado: quando não estiver segmentando o eixo
blockpadrão (como em um rolador horizontal), seja explícito sobre qual eixo rastrear comscroll(block)ouscroll(inline).
Quanto a este caso de uso específico:
- O elemento que você anima NÃO pode ser
position: staticouposition: relativeao usar porcentagens noanimation-range.- Isso ocorre porque esses elementos são considerados "em fluxo". Reduzir esses elementos ao rolar a tela diminuiria a distância total de rolagem, afetando o valor calculado de, por exemplo,
10%na rolagem.
- Isso ocorre porque esses elementos são considerados "em fluxo". Reduzir esses elementos ao rolar a tela diminuiria a distância total de rolagem, afetando o valor calculado de, por exemplo,
Suporte ao navegador e estratégias de substituição
Portanto, uma estratégia de fallback geralmente é necessária.
Para navegadores que não oferecem suporte a animações acionadas por rolagem, use um substituto para recriar os efeitos visuais. Os substitutos geralmente são criados com um listener de rolagem (para efeitos de ScrollTimeline) ou com a API IntersectionObserver (para efeitos de ViewTimeline).
Em navegadores com suporte integrado para animações de rolagem, use SEMPRE a implementação nativa de CSS, porque ela tem melhor desempenho.
Nem todos os efeitos podem ser recriados usando a abordagem de substitutos.
Para esse caso de uso específico, o script a seguir aplica o substituto para navegadores que não oferecem suporte a animações de rolagem. Ele usa um listener de rolagem para rastrear a posição de rolagem do documento em uma distância de 150px e atualiza a altura do cabeçalho de acordo.
// Fallback for browsers that don't support scroll-driven animations
if (!CSS.supports('(animation-timeline: scroll()) and (animation-range: 0% 100%)')) {
const header = document.querySelector('header');
const initialHeight = 200;
const finalHeight = 50;
const scrollDistance = 150;
window.addEventListener('scroll', () => {
const scrollY = window.scrollY;
const scrollPercent = Math.min(1, scrollY / scrollDistance);
const newHeight = initialHeight - (initialHeight - finalHeight) * scrollPercent;
header.style.height = `${newHeight}px`;
});
}