Texto longo, leia o resumo
A propriedade CSS overscroll-behavior
permite que os desenvolvedores modifiquem o
comportamento de rolagem flutuante padrão do navegador ao alcançar a parte de cima/baixo do
conteúdo. Os casos de uso incluem desativar o recurso "puxar para atualizar"
em dispositivos móveis, remover o brilho de rolagem e os efeitos de borracha e impedir que o conteúdo da página seja rolado quando estiver sob uma modal/sobreposição.
Contexto
Limites e encadeamento de rolagem
A rolagem é uma das maneiras mais fundamentais de interagir com uma página, mas certos padrões de UX podem ser complicados de lidar devido aos comportamentos padrão peculiares do navegador. Por exemplo, vamos usar uma gaveta de apps com um grande número de itens que o usuário pode precisar rolar. Quando eles chegam à parte de baixo, o contêiner flutuante para de rolar a tela porque não há mais conteúdo para consumir. Em outras palavras, o usuário atinge um "limite de rolagem". Mas observe o que acontece se o usuário continuar rolando. O conteúdo atrás da gaveta começa a rolar. A rolagem é assumida pelo contêiner pai, a própria página principal no exemplo.
Esse comportamento é chamado de encadeamento de rolagem, que é o comportamento padrão do navegador ao rolar conteúdo. Muitas vezes, o padrão é muito bom, mas às vezes não é desejável nem inesperado. Alguns apps podem querer oferecer uma experiência diferente quando o usuário atinge um limite de rolagem.
O efeito "puxar para atualizar"
"puxar para atualizar" é um gesto intuitivo e conhecido por apps para dispositivos móveis, como Facebook e Twitter. Puxar para baixo em um feed de redes sociais e liberar cria espaço para que as postagens mais recentes sejam carregadas. Na verdade, essa UX específica se tornou tão conhecida que navegadores para dispositivos móveis, como o Chrome no Android, tiveram o mesmo efeito. Deslizar para baixo no topo da página atualiza toda a página:
Para situações como o PWA do Twitter,
pode ser útil desativar a ação nativa de "puxar para atualizar". Por quê? Neste
app, é provável que você não queira que o usuário atualize a página acidentalmente. Também
é possível ver uma animação de atualização dupla. Como alternativa, pode
ser melhor personalizar a ação do navegador, alinhando-o mais de perto à marca do
site. O lado negativo é que esse tipo de personalização é
complicado de fazer. Os desenvolvedores acabam escrevendo JavaScript desnecessário, adicionam listeners de toque
não passivos (que bloqueiam a rolagem) ou mantêm a página inteira em um <div>
de 100 vw/vh (para evitar que a página estoure). Essas soluções alternativas têm
efeitos negativos bem documentados
sobre o desempenho da rolagem.
Podemos melhorar!
Conheça o overscroll-behavior
A propriedade overscroll-behavior
é um novo recurso CSS que controla
o comportamento do que acontece quando você rola a tela acima de um contêiner (incluindo a
própria página). É possível usá-lo para cancelar o encadeamento de rolagem, desativar/personalizar a
ação de arrastar para atualizar, desativar os efeitos de borracha no iOS (quando o Safari
implementa overscroll-behavior
) e muito mais.
A melhor parte é que usar overscroll-behavior
não afeta negativamente
o desempenho da página, como as dicas mencionadas na introdução.
A propriedade aceita três valores possíveis:
- auto: padrão. Os rolagens que se originam no elemento podem se propagar para elementos ancestrais.
- contain: evita o encadeamento de rolagem. As rolagens não se propagam para os ancestrais,
mas os efeitos locais dentro do nó são mostrados. Por exemplo, o efeito de brilho de rolagem
no Android ou o efeito de borracha no iOS, que notifica o usuário
quando ele atinge um limite de rolagem. Observação: o uso de
overscroll-behavior: contain
no elementohtml
impede ações de navegação de rolagem. - none: o mesmo que
contain
, mas também impede efeitos de rolagem dentro do próprio nó (por exemplo, brilho de rolagem do Android ou borracha de borracha do iOS).
Vamos conferir alguns exemplos para aprender a usar overscroll-behavior
.
Impedir que as rolagens escapem de um elemento de posição fixa
O cenário da caixa de chat
Considere uma caixa de chat fixa, posicionada na parte de baixo da página. A intenção é que a caixa de chat seja um componente independente e que ela role separadamente do conteúdo atrás dela. No entanto, devido ao encadeamento de rolagem, o documento começa a rolar assim que o usuário clica na última mensagem no histórico de chat.
Para este app, é mais apropriado que as rolagens originadas na
caixa de chat permaneçam dentro do chat. Podemos fazer isso adicionando
overscroll-behavior: contain
ao elemento que contém as mensagens do chat:
#chat .msgs {
overflow: auto;
overscroll-behavior: contain;
height: 300px;
}
Essencialmente, estamos criando uma separação lógica entre o contexto de rolagem da caixa de chat e a página principal. O resultado final é que a página principal permanece no mesmo lugar quando o usuário chega ao topo/fim do histórico de chat. As rolagens que começam na caixa de chat não são propagadas para fora.
Cenário de sobreposição de página
Outra variação do cenário de "underscroll" é quando há uma rolagem de conteúdo
por trás de uma sobreposição de posição fixa. Você acabou de ganhar um sorteio de
overscroll-behavior
! O navegador está tentando ser útil, mas
acaba fazendo o site parecer com bugs.
Exemplo - modal com e sem overscroll-behavior: contain
:
Como desativar o recurso "puxar para atualizar"
A desativação da ação de arrastar para atualizar é uma única linha de CSS. Basta impedir
o encadeamento de rolagem em todo o elemento de definição da janela de visualização. Na maioria dos casos, é
<html>
ou <body>
:
body {
/* Disables pull-to-refresh but allows overscroll glow effects. */
overscroll-behavior-y: contain;
}
Com essa adição simples, corrigimos as animações duplas de arrastar para baixo para atualizar na demonstração da caixa de chat e podemos implementar um efeito personalizado que usa uma animação de carregamento mais organizada. A caixa de entrada inteira também é desfocada à medida que ela é atualizada:
Confira um snippet do código completo:
<style>
body.refreshing #inbox {
filter: blur(1px);
touch-action: none; /* prevent scrolling */
}
body.refreshing .refresher {
transform: translate3d(0,150%,0) scale(1);
z-index: 1;
}
.refresher {
--refresh-width: 55px;
pointer-events: none;
width: var(--refresh-width);
height: var(--refresh-width);
border-radius: 50%;
position: absolute;
transition: all 300ms cubic-bezier(0,0,0.2,1);
will-change: transform, opacity;
...
}
</style>
<div class="refresher">
<div class="loading-bar"></div>
<div class="loading-bar"></div>
<div class="loading-bar"></div>
<div class="loading-bar"></div>
</div>
<section id="inbox"><!-- msgs --></section>
<script>
let _startY;
const inbox = document.querySelector('#inbox');
inbox.addEventListener('touchstart', e => {
_startY = e.touches[0].pageY;
}, {passive: true});
inbox.addEventListener('touchmove', e => {
const y = e.touches[0].pageY;
// Activate custom pull-to-refresh effects when at the top of the container
// and user is scrolling up.
if (document.scrollingElement.scrollTop === 0 && y > _startY &&
!document.body.classList.contains('refreshing')) {
// refresh inbox.
}
}, {passive: true});
</script>
Como desativar o brilho de rolagem e os efeitos de borracha
Para desativar o efeito de rejeição ao atingir um limite de rolagem, use
overscroll-behavior-y: none
:
body {
/* Disables pull-to-refresh and overscroll glow effect.
Still keeps swipe navigations. */
overscroll-behavior-y: none;
}
Demonstração completa
Juntando tudo, a
demonstração da caixa de chat completa usa
overscroll-behavior
para criar uma animação personalizada de deslizar para baixo para atualizar
e impedir que as rolagens saiam do widget da caixa de chat. Isso oferece uma experiência do usuário
ideal que seria difícil de alcançar sem o CSS
overscroll-behavior
.