English | 简体中文 | 繁體中文 | Русский язык | Français | Español | Português | Deutsch | 日本語 | 한국어 | Italiano | بالعربية

Efecto de seguimiento sincronizado de múltiples barras de desplazamiento con JavaScript nativo

En algunos sitios web que admiten la escritura de artículos en markdown, las páginas de escritura en la parte trasera generalmente admiten la previsualización instantánea de markdown, es decir, dividir toda la página en dos partes, la mitad izquierda es el texto markdown que ingresas, y la mitad derecha es la página de previsualización generada instantáneamente, por ejemplo, a continuación es el efecto de previsualización instantánea de markdown en la página de escritura en la parte trasera de CSDN:

Este artículo no explica cómo implementar este efecto desde cero (a continuación,) Es probable Se destacará el artículo, dejando de lado otros aspectos, únicamente se verán los dos elementos contenedores en el cuerpo de la página, es decir, el elemento de cuadro de entrada de markdown y el elemento de visualización de previsualización.

Lo que se va a explorar en este artículo es cómo, cuando el contenido de ambos elementos contenedores supera la altura del contenedor, es decir, cuando ambos aparecen en la caja de desplazamiento, cómo hacer que el otro elemento también se desplace cuando uno de ellos se desplace.

Estructura de DOM

Ya que se trata de lo relacionado con la barra de desplazamiento, lo primero que se piensa es una propiedad de control de altura de la barra de desplazamiento en js: scrollTop, siempre y cuando se pueda controlar el valor de esta propiedad, también se puede controlar el desplazamiento de la barra de desplazamiento.

Para la siguiente estructura de DOM:

<div id="container">
 <div class="left"></div>
 <div class="right"></div>
</div>

Dentro de esto, el elemento .left es el elemento de contenedor de entrada de la mitad izquierda, el elemento .right es el elemento de contenedor de visualización de la mitad derecha, y .container es el elemento padre común de ambos.

Debido a que se necesita un desplazamiento de desbordamiento, también se deben establecer los estilos correspondientes (solo los estilos clave, no todos):

#container {
 display: flex;
 border: 1px solid #bbb;
}
.left, .right {
 flex: 1;
 height: 100%;
 word-wrap: break-word;
 overflow-y: scroll;
}

Luego, se añade suficiente contenido a los elementos .left y .right para que aparezcan las barras de desplazamiento, y el efecto es el siguiente:

El estilo está más o menos establecido, por lo que se pueden realizar una serie de operaciones en estos DOM.

Primer intento

La idea general es escuchar los eventos de desplazamiento de dos elementos de contenedor, y cuando uno de ellos se desplaza, obtener el valor del atributo scrollTop de este elemento y establecer este valor como el valor de scrollTop del otro elemento de desplazamiento.

Por ejemplo:

var l=document.querySelector('.left')
var r=document.querySelector('.right')
l.addEventListener('scroll',function(){
 r.scrollTop = l.scrollTop
)

El efecto es el siguiente:

Parece bastante bueno, pero ahora no solo quiero que la derecha siga el desplazamiento de la izquierda, sino que también quiero que la izquierda siga el desplazamiento de la derecha, por lo que se agrega el siguiente código:

addEventListener('scroll',function(){
 r.scrollTop = l.scrollTop
)

Parece muy bueno, sin embargo, las cosas no son tan fáciles.

En este momento, al usar la rueda del ratón para desplazarse, se encuentra que es un poco difícil de desplazar, parece que se está frenando el desplazamiento de los dos elementos de contenedor.

Un análisis detallado muestra que la causa es sencilla: cuando se desplaza hacia la izquierda, se activa el evento de desplazamiento de la izquierda, por lo que la derecha sigue desplazándose, pero al mismo tiempo, el desplazamiento seguido de la derecha también es un desplazamiento, por lo que también se activa el desplazamiento de la derecha, y luego la izquierda también debe seguir desplazándose hacia la derecha... y luego se entra en una situación similar a la de activación mutua, por lo que resulta que es muy difícil desplazarse.

Solución al problema de que se activa el evento de desplazamiento scroll simultáneamente

Para resolver los problemas mencionados anteriormente, hay dos soluciones temporales.

Cambiar el evento scroll por el evento mousewheel

Dado que el evento scroll no solo se desencadena por el scroll activo del ratón, sino que también se desencadena por el cambio del scrollTop del elemento del contenedor, el scroll activo del elemento es realmente desencadenado por la rueda del ratón, por lo que se puede cambiar el evento scroll por un evento sensible a la rueda del ratón en lugar de sensible al desplazamiento del elemento: 'mousewheel', y el código de escucha mencionado anteriormente se convierte en:

addEventListener('mousewheel',function(){
  r.scrollTop = l.scrollTop
)
r.addEventListener('mousewheel',function(){
  l.scrollTop = r.scrollTop
)

El efecto es el siguiente:

Parece que es útil, pero en realidad hay dos problemas.

Cuando se hace scroll en un elemento de contenedor, el otro elemento de contenedor también sigue haciendo scroll, pero no lo hace suavemente, la altura tiene un salto instantáneo obvio

Después de buscar en línea, no encontré contenido relacionado con la frecuencia de scroll del evento 'wheel', supongo que esto es una característica de este evento.

Cada vez que el ratón rueda, básicamente no es con 1px, su unidad más pequeña es mucho más pequeña que la del evento scroll, cuando ruedo con mi ratón en el navegador Chrome, cada vez que ruedo, la distancia es 100px, diferentes ratones o navegadores deben tener este valor, y el evento 'wheel' realmente escucha el evento de que la rueda del ratón pasa por una pestaña de engranaje, lo que explica por qué ocurre el fenómeno de salto.

En general, cada vez que el ratón rueda una pestaña de engranaje, se puede escuchar un evento 'wheel', desde el principio hasta el final, el elemento que se ha arrastrado con el ratón ya ha desplazado 100px, por lo que el otro elemento de contenedor que sigue el scroll también saltará instantáneamente 100px

Y la razón por la que el evento scroll mencionado anteriormente no causará que los elementos que siguen el scroll salten instantáneamente es porque cada vez que el scrollTop del elemento que sigue el scroll cambia, su valor no tendrá 100px con un gran alcance, ni siquiera puede ser pequeño 1px, pero debido a su alta frecuencia de desencadenamiento y pequeño alcance de desplazamiento, al menos visualmente, es un desplazamiento suave.

El evento 'wheel' solo escucha eventos de la rueda del ratón, pero si se arrastra el scroll con el ratón, no se desencadenará este evento, y los otros elementos de contenedor también no seguirán el desplazamiento.

Este problema se puede resolver fácilmente, al arrastrar el scroll con el ratón, definitivamente se desencadenará el evento scroll, y en este caso, puedes determinar fácilmente a qué elemento de contenedor pertenece el scroll arrastrado, simplemente maneja el evento de scroll de este contenedor, y no es necesario manejar el evento de scroll del otro contenedor que sigue.

Compatibilidad del evento wheel

El evento wheel es DOM Level3 estándar de eventos, pero además de este evento, hay muchos eventos no estándar, diferentes motores de navegador utilizan diferentes estándares, por lo que es posible que se necesite realizar la compatibilidad según el caso, consulte MDN MouseWheelEvent para obtener más detalles.

Determinación en tiempo real

Si es difícil soportar el rebote de wheel y las diversas compatibilidades, realmente hay otra manera de hacerlo posible, sigue siendo el evento scroll, pero se necesita hacer un trabajo adicional.

El problema del evento scroll está en que no se puede determinar cuál es el elemento de contenedor de desplazamiento activo, una vez determinado el elemento de contenedor de desplazamiento activo, esto será fácil de manejar, por ejemplo, en el uso del evento wheel anterior, el uso del evento scroll se puede realizar fácilmente porque se puede determinar fácilmente qué es el elemento de contenedor de desplazamiento activo.

Por lo tanto, el problema clave radica en cómo determinar el elemento de contenedor de desplazamiento activo actual, una vez resuelto este problema, todo lo demás será fácil de manejar.

Ya sea que el ratón desplace la rueda de desplazamiento o arrastre la barra de desplazamiento, se desencadenará el evento scroll, y en este momento, en el eje Z del sistema de coordenadas, las coordenadas del ratón ciertamente estarán dentro del área ocupada por el elemento de contenedor de desplazamiento, es decir, en el eje Z, el ratón está flotando o ubicado sobre el elemento de contenedor de desplazamiento.

Cuando el ratón se mueve en la pantalla, es posible obtener las coordenadas actuales del ratón.

Entre ellos, clientX y clientY son las coordenadas del ratón en relación con la ventana de visualización, se puede considerar que si esta coordenada está dentro del rango de algún contenedor de desplazamiento, se considera que este elemento de contenedor es el elemento de desplazamiento activo, y el rango de coordenadas del elemento de contenedor se puede obtener utilizando getBoundingClientRect.

A continuación, se muestra un ejemplo de código cuando el ratón se mueve sobre el elemento .left:

if (e.clientX>l.left && e.clientX<l.right && e.clientY>l.top) {
  // Entrar en el elemento .left
}

Claro que es posible, pero teniendo en cuenta que dos elementos de contenedor de desplazamiento ocupan prácticamente toda la superficie de la pantalla, la superficie que el mousemove debe escuchar es demasiado grande, lo que puede requerir un rendimiento alto, por lo que en realidad se puede cambiar por el evento mouseover, solo es necesario escuchar si el ratón entra o sale de algún elemento de contenedor de desplazamiento, y también se evita la determinación de las coordenadas anterior.

addEventListener('mouseover',function(){
 // Entrar al elemento del contenedor de desplazamiento .left
)

Cuando se determina qué elemento del contenedor se está desplazando con el mouse, solo se necesita manejar el evento de desplazamiento de este contenedor, y no se necesita manejar el evento de desplazamiento del otro contenedor que sigue.

Sí, el efecto es muy bueno, el rendimiento también es bueno, perfect, podemos terminar.

Desplazamiento proporcional

Todos los ejemplos anteriores son efectos en el caso de que la altura del contenido de dos elementos de contenedor deslizante sea completamente idéntica, ¿qué pasa si la altura del contenido de estos dos elementos de contenedor deslizante es diferente?

Ese es el siguiente efecto:

Como se puede ver, debido a que la altura del contenido de dos elementos de contenedor deslizante es diferente, la altura máxima de scrollTop también es diferente, lo que resulta en que cuando uno de los elementos con un valor de scrollTop más pequeño se desliza hasta el final, el otro elemento aún se encuentra a la mitad, o cuando uno de los elementos con un valor de scrollTop más grande solo se desliza hasta la mitad, el otro elemento ya ha llegado al final.

Esta situación es muy común, por ejemplo, cuando escribes con markdown, una marca de título de nivel uno # en el modo de edición generalmente ocupa una altura menor que la ocupada en el modo de vista previa, lo que resulta en una altura de desplazamiento desigual en los lados derecho e izquierdo.

Por lo tanto, si también se considera esta situación, no se puede simplificar simplemente estableciendo valores scrollTop entre dos elementos de contenedor deslizante.

Aunque no se puede fijar la altura del contenido del contenedor deslizante, hay un punto que se puede determinar, la altura máxima de desplazamiento del scroll, o el valor de scrollTop, definitivamente tiene una cierta relación con la altura del contenido del contenedor deslizante y la altura del contenedor deslizante en sí.

Dado que se necesita saber la altura del contenido del contenedor deslizante y también debe existir una barra de desplazamiento, se necesita agregar un elemento hijo a este elemento del contenedor, la altura del elemento hijo no es limitada, es la altura del contenido del contenedor deslizante, la altura del contenedor es fija, se puede desplazar cuando hay sobrecarga.

<div id="container">
 <div class="left">
	 <div class="child"></div>
 </div>
 <div class="right">
	 <div class="child"></div>
 </div>
</div>

El ejemplo de estructura es el siguiente:

A través de mi observación, inferencia y verificación práctica, se ha determinado su relación, que es muy simple, es la operación básica de suma y resta:

la altura máxima de desplazamiento del scroll (scrollTopMax) = la altura del contenido del contenedor deslizante (es decir, la altura del elemento hijo ch) - la altura del contenedor deslizante en sí (es decir, la altura del elemento del contenedor ph)

es decir

Esto es decir, si se ha determinado la altura del contenido del contenedor deslizante (es decir, la altura del elemento hijo ch) y la altura del contenedor deslizante en sí (es decir, la altura del elemento del contenedor ph), entonces se puede determinar con seguridad la altura máxima de desplazamiento del scroll (scrollTop), y estos dos valores de altura básicamente se pueden obtener, por lo que se puede obtener scrollTop

Por lo tanto, para que dos contenedores de elementos de desplazamiento se desplacen verticalmente en proporción, es decir, cuando uno de los elementos se desplace al final o al fondo, el otro elemento también debe desplazarse al final o al fondo, entonces solo se necesita obtener la proporción de la mayor valor de scrollTop entre los dos contenedores de desplazamiento de rolling.

Después de determinar la escala, en el momento del desplazamiento en tiempo real, solo es necesario obtener el scrollTop del contenedor de desplazamiento activo.1 , se puede obtener el scrollTop correspondiente del otro contenedor de desplazamiento de seguimiento.2 :

Una vez que se aclara la idea, escribir código es una tarea fácil, el efecto es el siguiente:

Muy suave~

Conclusión

Se ha implementado el requisito anterior, pero es posible que necesite realizar ciertas modificaciones según la situación real en el proceso de práctica, por ejemplo, si escribe una página de edición y visualización en línea de markdown, necesita actualizar el valor de scale en tiempo real según la altura del contenido de entrada. Sin embargo, el tema principal ya está resuelto y las modificaciones menores no son difíciles.

Además, lo mencionado en este artículo no solo se aplica a dos elementos de contenedor de desplazamiento de seguimiento, sino que también se puede expandir para que el seguimiento de desplazamiento entre más elementos también se pueda implementar según la idea de este artículo. Este artículo solo se ha detallado en dos elementos para facilitar la explicación.

Resumen

Lo mencionado anteriormente es el efecto de seguimiento de desplazamiento sincronizado de múltiples barras de desplazamiento nativos que el editor le ha presentado a todos, esperamos que sea útil para ustedes. Si tienen alguna pregunta, déjenme un mensaje y el editor les responderá a tiempo. También agradezco mucho el apoyo a la tutorial de alarido!

Declaración: El contenido de este artículo se ha obtenido de la red, es propiedad del autor original, el contenido ha sido contribuido y subido por los usuarios de Internet, este sitio no posee los derechos de propiedad, no ha sido editado por humanos y no asume ninguna responsabilidad legal relacionada. Si encuentra contenido sospechoso de infracción de derechos de autor, por favor envíe un correo electrónico a: notice#oldtoolbag.com (al enviar un correo electrónico, por favor reemplace # con @) para denunciar y proporcionar evidencia relevante. Una vez confirmado, este sitio eliminará inmediatamente el contenido sospechoso de infracción.

Tutorial de Elasticsearch