Um indicador de progresso de rolagem é um padrão comum de interface do usuário que comunica visualmente o progresso do usuário em um documento ou contêiner rolável. Conforme o usuário rola a tela, um elemento visual é atualizado para refletir a posição dele, fornecendo uma noção clara e intuitiva de quanto conteúdo foi visualizado e quanto ainda resta.
Como fazer a implementação
Para criar um indicador de progresso de rolagem, você precisa de duas coisas:
- Um elemento para atuar como a barra de progresso. Esse elemento normalmente é
position: fixedouposition: absolutepara que permaneça visível enquanto o usuário rola a página. - Uma animação vinculada à posição de rolagem.
Confira como fazer isso:
- Primeiro, crie um elemento HTML que vai servir como barra de progresso. Esse elemento pode ser estilizado como você quiser.
- Em seguida, no CSS, defina uma animação
@keyframesque dimensione a barra de progresso. Uma abordagem comum é dimensionar o elemento descaleX(0)parascaleX(1). - Por fim, aplique essa animação ao elemento da barra de progresso e defina o
animation-timelinecomo uma linha do tempo de rolagem. Isso informa ao navegador para direcionar o progresso da animação com base na posição de rolagem do ancestral mais próximo.
Exemplo de código
Esse código aumenta o elemento #progress na rolagem usando uma linha do tempo de rolagem anônima, criada pela função scroll().
@media (prefers-reduced-motion: no-preference) {
@supports ((animation-timeline: scroll())) {
@keyframes grow-progress {
from { transform: scaleX(0); }
to { transform: scaleX(1); }
}
#progress {
position: fixed;
left: 0; top: 0;
width: 100%; height: 1em;
background: red;
transform-origin: 0 50%;
animation: grow-progress auto linear;
animation-timeline: scroll();
}
}
}
Devido à localização no DOM, a função scroll() vai rastrear o scroller ancestral mais próximo na direção block, que aqui é o scroller raiz.
<body>
<!-- MANDATORY: Purely decorative visual scroll progress bars MUST set aria-hidden="true" to remove the empty element from the assistive technology reading tree -->
<div id="progress" aria-hidden="true"></div>
</body>
Esse código aumenta o elemento #progress na rolagem usando uma linha do tempo de rolagem nomeada, criada pela propriedade scroll-timeline.
@media (prefers-reduced-motion: no-preference) {
@supports ((animation-timeline: scroll())) {
@keyframes grow-progress {
from { transform: scaleX(0); }
to { transform: scaleX(1); }
}
:root {
scroll-timeline: --tl block;
}
#progress {
position: fixed;
left: 0; top: 0;
width: 100%; height: 1em;
background: red;
transform-origin: 0 50%;
animation: grow-progress auto linear;
animation-timeline: --tl;
}
}
}
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())para verificar a compatibilidade e fornecer um substituto para navegadores que não oferecem suporte.- 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.
- NÃO use o pacote
- REMOVA elementos puramente decorativos dos fluxos de leitura de tecnologia adaptativa: aplique
aria-hidden="true"a indicadores de rolagem puramente visuais para garantir que os leitores de tela não encontrem nós vazios e sem nome. - 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. - RECOMENDADO: use a ordem de declaração correta. Ao usar a propriedade abreviada
animation, declareanimation-timelinedepois 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).
Ao usar a propriedade scroll-timeline para criar uma animação acionada por rolagem:
- USE um CSS
<dashed-ident>para o nome. - OPCIONAL: seja explícito sobre o eixo a ser rastreado. Quando não estiver segmentando o eixo
blockpadrão (como em um scroller horizontal), seja explícito sobre qual eixo rastrear comscroll-timeline-axis. - VERIFIQUE se o escopo da pesquisa funciona: quando o elemento que declara o
scroll-timelinenão é um ancestral de árvore simples do elemento animado, eleve a visibilidade do nome doscroll-timelineusandotimeline-scopeem um ancestral compartilhado.
Estratégias de fallback
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 elemento raiz e atualiza a propriedade transform da barra de progresso de acordo.
<script>
if (!CSS.supports('animation-timeline', 'scroll()')) {
const progress = document.querySelector('#progress');
window.addEventListener('scroll', () => {
const scrollable = document.documentElement.scrollHeight - window.innerHeight;
const scrolled = window.scrollY;
const progressPercentage = (scrolled / scrollable);
progress.style.transform = `scaleX(${progressPercentage})`;
});
}
</script>