Controla tu desplazamiento: personaliza los efectos de "deslizar hacia abajo para actualizar" y "desbordamiento",

Resumen

La propiedad overscroll-behavior de CSS permite a los desarrolladores anular el comportamiento de desplazamiento de desbordamiento predeterminado del navegador cuando se llega a la parte superior o inferior del contenido. Entre los casos de uso, se incluyen inhabilitar la función "deslizar para actualizar" en dispositivos móviles, quitar el brillo de sobredesplazamiento y los efectos de bandas elásticas, y evitar que el contenido de la página se desplace cuando se encuentre debajo de una superposición modal o una superposición.

Información general

Límites de desplazamiento y encadenamiento de desplazamientos

Encadenamiento de desplazamiento en Chrome para Android.

El desplazamiento es una de las formas más fundamentales de interactuar con una página, pero ciertos patrones de UX pueden ser difíciles de manejar debido a los comportamientos predeterminados peculiares del navegador. Tomemos como ejemplo un panel de apps con una gran cantidad de elementos por los que el usuario podría tener que desplazarse. Cuando llegan a la parte inferior, el contenedor de desbordamiento deja de desplazarse porque no hay más contenido para consumir. En otras palabras, el usuario alcanza un "límite de desplazamiento". Pero observa lo que sucede si el usuario continúa desplazándose. El contenido detrás del panel lateral comienza a desplazarse. El contenedor superior se encarga del desplazamiento; la página principal en el ejemplo.

Al parecer, este comportamiento se llama encadenamiento de desplazamiento; es el comportamiento predeterminado del navegador cuando se desplaza por el contenido. A menudo, el valor predeterminado es bastante bueno, pero a veces no es conveniente o incluso inesperado. Es posible que algunas apps quieran proporcionar una experiencia del usuario diferente cuando el usuario llega a un límite de desplazamiento.

Efecto de "deslizar hacia abajo para actualizar"

El gesto "deslizar hacia abajo para actualizar" es un gesto intuitivo popularizado en apps para dispositivos móviles, como Facebook y Twitter. Deslizarse hacia abajo en un feed social y lanzarlo crea un nuevo espacio para que se carguen las publicaciones más recientes. De hecho, esta UX en particular se ha vuelto tan popular que los navegadores para dispositivos móviles como Chrome en Android adoptaron el mismo efecto. Si deslizas el dedo hacia abajo en la parte superior de la página, se actualizará por completo:

Opción "deslizar para actualizar" personalizada de Twitter
cuando se actualiza un feed en la AWP.
La acción nativa de "deslizar hacia abajo para actualizar" de Chrome Android
actualiza toda la página.

En situaciones como la AWP de Twitter, tiene sentido inhabilitar la acción nativa de "deslizar hacia abajo para actualizar". ¿Por qué? En esta app, probablemente no quieras que el usuario actualice la página por accidente. También existe la posibilidad de ver una animación de actualización doble. También puede ser mejor personalizar la acción del navegador para alinearlo mejor con la marca del sitio. La mala noticia es que este tipo de personalización ha sido difícil de lograr. Los desarrolladores terminan escribiendo código JavaScript innecesario, agregan objetos de escucha táctiles no pasivos (que bloquean el desplazamiento) o pegan toda la página en una <div> de 100vw/vh (para evitar que la página se desborde). Estas soluciones tienen efectos negativos bien documentados en el rendimiento del desplazamiento.

¡Podemos hacerlo mejor!

Presentamos overscroll-behavior

La propiedad overscroll-behavior es una nueva función de CSS que controla el comportamiento de lo que sucede cuando te desplazas sobre un contenedor (incluida la página en sí). Puedes usarlo para cancelar el encadenamiento de desplazamiento, inhabilitar o personalizar la acción de "deslizar hacia abajo para actualizar", inhabilitar los efectos de bandas elásticas en iOS (cuando Safari implementa overscroll-behavior) y mucho más. La mejor parte es que usar overscroll-behavior no afecta negativamente el rendimiento de la página, como los hackeos mencionados en la introducción.

La propiedad toma tres valores posibles:

  1. auto: Es el valor predeterminado. Los desplazamientos que se originan en el elemento pueden propagarse a los elementos principales.
  2. contain: Evita el encadenamiento de desplazamiento. Los desplazamientos no se propagan a los principales, pero se muestran los efectos locales dentro del nodo. Por ejemplo, el efecto de resplandor de sobredesplazamiento en Android o el efecto de bandas elásticas en iOS, que notifica al usuario cuando llega a un límite de desplazamiento. Nota: El uso de overscroll-behavior: contain en el elemento html evita las acciones de navegación de desplazamiento.
  3. none: Es igual que contain, pero también evita los efectos de sobredesplazamiento dentro del nodo (p.ej., el brillo de sobredesplazamiento de Android o la banda elástica de iOS).

Analicemos algunos ejemplos para ver cómo usar overscroll-behavior.

Cómo evitar que los desplazamientos escapen de un elemento de posición fija

El escenario de los cuadros de chat

También se desplaza el contenido debajo de la ventana de chat :(

Piensa en un cuadro de chat de posición fija que se encuentra en la parte inferior de la página. La intención es que la casilla de chat sea un componente autónomo y se desplaza por separado del contenido detrás de ella. Sin embargo, debido al encadenamiento de desplazamiento, el documento comienza a desplazarse en cuanto el usuario recibe el último mensaje del historial de chat.

Para esta app, es más apropiado que los desplazamientos que se originan en el cuadro de chat permanezcan en él. Para ello, agregamos overscroll-behavior: contain al elemento que contiene los mensajes de chat:

#chat .msgs {
  overflow: auto;
  overscroll-behavior: contain;
  height: 300px;
}

En esencia, creamos una separación lógica entre el contexto de desplazamiento del cuadro de chat y la página principal. El resultado final es que la página principal permanece en su lugar cuando el usuario llega a la parte superior o inferior del historial de chat. Los desplazamientos que comienzan en el cuadro de chat no se propagan.

Situación de superposición de página

Otra variación del escenario de "desplazamiento" es cuando ves contenido que se desplaza detrás de una superposición de posición fija. ¡Un sorteo no entregado overscroll-behavior está en orden! El navegador intenta ser útil, pero termina haciendo que el sitio parezca defectuoso.

Ejemplo: modal con y sin overscroll-behavior: contain:

Antes: El contenido de la página se desplaza debajo de la superposición.
Después: El contenido de la página no se desplaza debajo de la superposición.

Cómo inhabilitar la función "deslizar hacia abajo para actualizar"

Desactivar la acción "deslizar hacia abajo para actualizar" es una sola línea de CSS. Solo evita el encadenamiento de desplazamiento en todo el elemento definido por el viewport. En la mayoría de los casos, es <html> o <body>:

body {
  /* Disables pull-to-refresh but allows overscroll glow effects. */
  overscroll-behavior-y: contain;
}

Con esta simple adición, corrigemos las animaciones de doble deslizamiento para actualizar en la demostración de la casilla de chat y, en su lugar, podemos implementar un efecto personalizado que use una animación de carga más prolija. Toda la bandeja de entrada también se desenfoca a medida que esta se actualiza:

Antes
Después

Este es un fragmento del 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>

Inhabilita los efectos de brillo y de bandas elásticas de sobredesplazamiento

Para inhabilitar el efecto de rebote cuando se alcanza un límite de desplazamiento, usa overscroll-behavior-y: none:

body {
  /* Disables pull-to-refresh and overscroll glow effect.
     Still keeps swipe navigations. */
  overscroll-behavior-y: none;
}
Antes: Cuando se presiona el límite de desplazamiento, se muestra un resplandor.
Después: Se inhabilitó el resplandor.

Demostración completa

En resumen, la demostración del cuadro de chat completa usa overscroll-behavior para crear una animación personalizada de "deslizar hacia abajo para actualizar" y, luego, inhabilitar los desplazamientos para que no se escapen del widget del cuadro de chat. Esto proporciona una experiencia del usuario óptima que habría sido difícil de lograr sin CSS overscroll-behavior.

Ver demostración | Fuente