Complejidades de un desplazador infinito

Resumen: Reutiliza los elementos del DOM y quita los que están lejos de la viewport. Usa marcadores de posición para los datos retrasados. Este es un demo y el código para infinito barra de desplazamiento.

Aparecen ventanas emergentes de desplazamiento infinito en Internet. La lista de artistas de Google Music es uno, la línea de tiempo de Facebook es uno y el feed en vivo de Twitter también es uno. Tú desplázate hacia abajo y, antes de llegar al final, aparecerá mágicamente contenido nuevo. aparentemente de la nada. Es una experiencia fluida para los usuarios y es fácil a ver la apelación.

Sin embargo, el desafío técnico detrás de un desplazador infinito es más difícil que parece. La variedad de problemas que encuentra cuando desea hacer lo correctoTM es amplio. Comienza con elementos simples, como que los enlaces en el pie de página se convierten en prácticamente inalcanzables, ya que el contenido sigue alejando el pie de página. Sin embargo, los problemas se vuelven más difíciles. ¿Cómo se controla un evento de cambio de tamaño cuando alguien cambia su tu teléfono de la orientación vertical a la horizontal se detuviera cuando la lista es demasiado larga?

Lo correctoTM

Pensamos que era suficiente para crear una implementación de referencia que muestra una forma de abordar todos estos problemas de una manera reutilizable mientras manteniendo los estándares de rendimiento.

Vamos a usar 3 técnicas para lograr nuestro objetivo: reciclaje de DOM, tombstones. y el anclado de desplazamiento.

Nuestro caso de demostración será una ventana de chat similar a Hangouts en la que podemos desplazarnos a través de los mensajes. Lo primero que necesitamos es una fuente infinita de chat mensajes nuevos. Técnicamente, ninguno de los desplazamientos infinitos que existen son realmente. infinito, pero con la cantidad de datos disponibles para enviarlos a estas manuscritos que también podrían ser. Por razones de simplicidad, codificaremos grupo de mensajes de chat y elige el mensaje, el autor y ocasionalmente la imagen adjunta en aleatorias con una pizca de retraso artificial para que se comporte de Google Cloud.

Captura de pantalla de la app de Chat

Reciclaje de DOM

El reciclaje del DOM es una técnica poco utilizada para mantener bajo el recuento de nodos del DOM. El se suelen usar elementos del DOM ya creados que están fuera de la pantalla. de crear proyectos nuevos. Es cierto que los nodos del DOM son económicos, pero son no son gratuitos, ya que cada uno agrega costos adicionales de memoria, diseño, estilo y pintura. Los dispositivos de gama baja se volverán mucho más lentos si no son completamente inutilizables si la tiene un DOM demasiado grande para administrarlo. Además, ten en cuenta que cada rediseño y la nueva aplicación de tus estilos; es un proceso que se activa cada vez que una clase se agrega o se quita de un nodo y aumenta su costo con un DOM más grande. Al reciclar los nodos del DOM, mantendremos el número total de DOM de nodos considerablemente más bajos, lo que agiliza todos estos procesos.

El primer obstáculo es el desplazamiento en sí. Como solo tendremos un pequeño subconjunto de todos los elementos disponibles del DOM en cualquier momento, necesitamos encontrar otra manera para que la barra de desplazamiento del navegador refleje correctamente la cantidad de contenido que se en teoría. Usaremos un elemento sentinel de 1 px por 1 px con una transformación para forzar que el elemento que contiene los elementos (la pasarela) tenga la altura. Promocionaremos cada elemento de la pasarela a su propia capa para que asegurarse de que la capa de la pasarela en sí misma esté completamente vacía. Sin color de fondo, nada. Si la capa de la pasarela no está vacía, no es apta para la ubicación optimizaciones y tendremos que almacenar una textura en nuestra tarjeta gráfica que tenga una altura de cientos de mil píxeles. Definitivamente no es viable en una dispositivo móvil.

Cada vez que nos desplacemos, verificaremos si el viewport se acercó lo suficiente a el final de la pasarela. Si es así, extenderemos la pista moviendo el centinela y se moverán los elementos que salieron del viewport a la parte inferior de y completarlos con contenido nuevo.

Pasarela Sentinel Ventana de visualización

Lo mismo ocurre con el desplazamiento en la dirección opuesta. Sin embargo, nunca reducir el recorrido en nuestra implementación para que la posición de la barra de desplazamiento permanezca coherentes.

Lápidas

Como mencionamos antes, tratamos de hacer que nuestra fuente de datos se comporte como algo en en el mundo real. Con latencia de red y todo lo demás. Eso significa que si nuestro los usuarios hacen uso del desplazamiento rápido, pueden desplazarse más allá del último elemento de los que tenemos datos. Si eso sucede, colocaremos un elemento de lápida, un marcador de posición, que será reemplazado por el elemento con contenido real una vez que llegaron los datos. Las lápidas también se reciclan y tienen una piscina separada para elementos de DOM reutilizables. La necesitamos para poder hacer una buena transición o lápida al artículo con contenido que, de otro modo, sería muy molestar al usuario y hacer que pierda de vista lo que nos concentraremos.

Tales
de tumbas. Muy piedra. ¡Vaya!

Un desafío interesante es que los elementos reales pueden tener una altura mayor que el elemento de la lápida debido a la cantidad de texto por elemento o un archivo adjunto imagen. Para resolver esto, ajustaremos la posición actual de desplazamiento cada vez llegan datos y se reemplaza una tombstone sobre el viewport, el anclaje la posición de desplazamiento a un elemento en lugar de a un valor de píxel. Este concepto es llamado anclado de desplazamiento.

Anclaje de desplazamiento

Nuestro anclado de desplazamiento se invocará tanto cuando las lápidas se reemplacen como y cuando se cambia el tamaño de la ventana (lo que también ocurre cuando se cambia el tamaño se está dando vuelta). Tendremos que averiguar cuál es el elemento visible superior es el viewport. Como ese elemento solo podría ser parcialmente visible, también almacenar el desplazamiento desde la parte superior del elemento donde comienza el viewport.

Diagrama de anclaje de desplazamiento.

Si se cambia el tamaño del viewport y la pista se modifica, podemos restablecer situación que se sienta visualmente idéntica para el usuario. y gana. Excepto en caso de un ventana significa que cada elemento posiblemente ha cambiado su altura, entonces, ¿cómo sepa a qué distancia debe colocarse el contenido fijo? Nosotros no. Para averiguarlo tendríamos que diseñar cada elemento sobre el elemento anclado y sumar todos sus alturas; podría causar una pausa significativa después de cambiar el tamaño, y no quieren eso. En cambio, asumimos que todos los elementos anteriores tienen el mismo tamaño. como una lápida y ajustar nuestra posición de desplazamiento en consecuencia. Como los elementos nos desplazamos a la pasarela, ajustamos nuestra posición de desplazamiento y diferimos para que el diseño funcione hasta que se lo necesite.

Diseño

Omití un detalle importante: el diseño. Cada reciclaje de un elemento del DOM que normalmente rediseñaría toda la pasarela, lo que estaría muy por debajo de nuestros objetivo de 60 fotogramas por segundo. Para evitarlo, tomamos la carga de diseñar sobre nosotros mismos y usar elementos absolutamente posicionados con transformaciones. Así, podemos suponer que todos los elementos más arriba en la pasarela ocupan espacio cuando, en realidad, solo hay espacio vacío. Como estamos haciendo diseños propios, podemos almacenar en caché las posiciones donde termina cada elemento y podemos cargar de inmediato el elemento correcto desde la caché cuando el usuario se desplaza hacia atrás.

Idealmente, los elementos solo se volverán a pintar una vez cuando se adjunten al DOM. ni que se desanimen las adiciones o eliminaciones de otros objetos en la pasarela. Es decir, posible, pero solo con los navegadores actualizados.

Ajustes sorprendentes

Recientemente, Chrome agregó compatibilidad con la contención de CSS, una función que nos permite a los desarrolladores indicarle al navegador que un elemento es un límite para el diseño y la pintura. Como nosotros nosotros hacemos el diseño aquí, es una gran una solicitud de contención. Cada vez que agregamos un elemento a la pasarela, sabemos no es necesario que el cambio de diseño afecte los demás elementos. Así que cada elemento debería obtener contain: layout. Tampoco queremos afectar al resto del sitio web, así que la pasarela también debería tener esta directiva de estilo.

También evaluamos cómo usar IntersectionObservers como mecanismo para detectar cuándo el usuario se desplazó lo suficiente como para que nosotros empecemos a reciclar elementos y cargar nuevos de datos no estructurados. Sin embargo, IntersectionObservers se especifica para tener una latencia alta (como con requestIdleCallback), por lo que podríamos sentir menos receptivo si IntersectionObservers que sin ellos. Incluso nuestra implementación actual usando scroll se ve afectado por este problema, ya que los eventos de desplazamiento se envían en un “mejor esfuerzo”. Con el tiempo, el Worklet compositor de Houdini sería la solución de alta fidelidad a este problema.

Aún no es perfecta

Nuestra implementación actual de reciclaje del DOM no es ideal, ya que agrega todos los elementos. que pasan por el viewport, en lugar de preocuparse solo por realmente en la pantalla. Esto significa que cuando te desplazas realmente rápido, tanto trabajo para el diseño y la pintura en Chrome que no puede seguirle el ritmo. Finalizarás solo ven el fondo. No es el fin del mundo, pero sin dudas es algo que mejorar.

Esperamos que veas lo desafiantes que pueden ser los problemas simples cuando quieres combinan una gran experiencia del usuario con altos estándares de rendimiento. Con Las Progressive Web Apps se convertirán en experiencias centrales en los teléfonos celulares. será cada vez más importante y los desarrolladores web deberán seguir invirtiendo en con patrones que respeten las restricciones de rendimiento.

Encontrarás todo el código en nuestro repositorio. Ya hicimos hacemos todo lo posible para que sea reutilizable, pero no lo publicaremos como una biblioteca real en npm o como un repositorio independiente. El uso principal es educativo.