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

Effetto di sincronizzazione di scorrimento di più scrollbar con JavaScript nativo

In alcuni siti web che supportano la scrittura di articoli in markdown, le pagine di scrittura dietro le quinte, di solito supportano l'anteprima immediata di markdown, ovvero dividere l'intera pagina in due parti, la parte sinistra è il testo markdown che si inserisce, e la parte destra è l'anteprima immediata della pagina di visualizzazione corrispondente, ad esempio, l'effetto di anteprima immediata di markdown della pagina di scrittura dietro le quinte di CSDN è il seguente:

Questo articolo non spiega come implementare tale effetto da zero (successivo) Probabilmente Il testo dell'articolo,), lasciando da parte altri, guardiamo solo i due elementi contenitori principali della pagina, ovvero l'elemento casella di input markdown e l'elemento di visualizzazione di anteprima.

Il tema di questo articolo è come gestire il caso in cui il contenuto di entrambi gli elementi contenitori supera l'altezza del contenitore, ovvero quando entrambi appaiono i riquadri di scorrimento, come far sì che uno degli elementi contenitori scorra mentre l'altro elemento lo segue.

Struttura DOM

Poiché riguarda la barra di scorrimento, il primo pensiero è l'attributo di controllo dell'altezza della barra di scorrimento in JavaScript: scrollTop, controllando il valore di questa proprietà, è possibile controllare il movimento della barra di scorrimento.

Per la seguente struttura DOM:

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

Tra cui, l'elemento .left è l'elemento contenitore della casella di input della metà sinistra, l'elemento .right è l'elemento contenitore della casella di visualizzazione della metà destra, .container è il loro elemento genitore comune.

Poiché è necessario impostare il scorrimento overflow, è necessario impostare alcune stili corrispondenti (solo stili chiave, non tutti):

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

Aggiungere ancora contenuti sufficienti agli elementi .left e .right in modo che entrambi appaiano le barre di scorrimento, è proprio come questo effetto:

Lo stile è uscito più o meno, quindi si possono eseguire una serie di operazioni su questi DOM.

Primo tentativo

La linea di pensiero generale è di ascoltare gli eventi di scorrimento di due elementi contenitori, quando uno di questi elementi si muove, ottenere il valore dell'attributo scrollTop di questo elemento e impostare questo valore come il valore di scrollTop dell'altro elemento di scorrimento.

Ad esempio:

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

L'effetto è il seguente:

Sembra molto buono, ma ora non si vuole solo che il lato destro segua lo scorrimento del lato sinistro, ma anche che il lato sinistro segua lo scorrimento del lato destro, quindi si aggiunge il seguente codice:

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

Sembra molto buono, tuttavia, non è così semplice.

In questo momento, quando si utilizza la rotella del mouse per scorrere, si scopre che lo scorrimento è un po' difficile, come se i due elementi contenitori di scorrimento fossero bloccati da qualcosa, è difficile scorrere.

Un'analisi attenta rivela che la causa è semplice: quando si scrolla a sinistra, viene attivato l'evento di scorrimento sinistro, e quindi il lato destro segue lo scorrimento, ma allo stesso tempo lo scorrimento del lato destro è anche uno scorrimento, quindi viene attivato l'evento di scorrimento del lato destro, quindi il lato sinistro deve seguire il lato destro...e poi si entra in una situazione simile a una catena di scatenamenti. Pertanto, si scopre che lo scorrimento è abbastanza difficile.

Risolvere il problema di attivazione simultanea dell'evento scroll

Per risolvere i problemi menzionati, ci sono temporaneamente due soluzioni.

Sostituire l'evento scroll con l'evento mousewheel

Poiché l'evento scroll può essere attivato non solo dall'azione attiva del mouse, ma anche dalla modifica del scrollTop dell'elemento del contenitore, l'azione attiva dell'elemento è in realtà attivata dal rotellino del mouse, quindi è possibile sostituire l'evento scroll con un evento sensibile alla rotazione del mouse piuttosto che all'azione dell'elemento: 'mousewheel', quindi il codice di ascolto menzionato sopra diventa:

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

L'effetto è il seguente:

Sembra essere utile, ma in realtà ci sono due problemi.

Quando si scivola uno degli elementi del contenitore, l'altro contenitore di scorrimento si muove di conseguenza, ma lo scorrimento non è fluido, e c'è un salto istantaneo significativo dell'altezza.

Ho cercato in rete, ma non ho trovato contenuti relativi alla frequenza di scorrimento dell'evento wheel, suppongo che questo possa essere una feature di questo evento.

Ogni volta che il mouse scende, di solito non è unito a 1px, la sua unità minima è molto più piccola rispetto all'evento scroll, quando uso il mio mouse per scorrere nel browser Chrome, la distanza scorsa ogni volta è esattamente 100px, questo valore dovrebbe essere diverso per diversi mouse o browser, e l'evento wheel ascolta effettivamente l'evento in cui la rotella del mouse passa attraverso un punto di blocco del ingranaggio, questo può spiegare perché si verifica il fenomeno di salto.

Di solito, ogni volta che la rotella del mouse gira attraverso un punto di blocco del ingranaggio, si può ascoltare un evento wheel, dall'inizio alla fine, l'elemento trascinato attivamente dal mouse ha scollato 100px, quindi l'altro contenitore di scorrimento di accompagnamento si muove di 100px istantaneamente.

Il motivo per cui l'evento scroll menzionato sopra non fa apparire il saltello istantaneo dell'elemento di scorrimento di accompagnamento è perché ogni volta che il valore scrollTop dell'elemento di scorrimento di accompagnamento cambia, il suo valore non avrà una differenza di 100px, né sarà piccolo quanto 1px, ma a causa della frequenza di attivazione alta e della larghezza di scorrimento piccola, almeno visivamente è uno scorrimento liscio.

wheel ascolta solo gli eventi di rotazione della rotella del mouse, ma se si trascina la barra di scorrimento con il mouse, non verrà attivato questo evento, e altri elementi del contenitore non seguiranno lo scorrimento.

Questo è in realtà molto facile da risolvere, trascinando la barra di scorrimento con il mouse è sicuro che attiverà l'evento scroll, e in questo caso, è facile determinare quale contenitore elemento è il barra di scorrimento trascinata, è sufficiente gestire l'evento di scorrimento di questo contenitore, e non trattare l'evento di scorrimento del contenitore di accompagnamento.

Problemi di compatibilità dell'evento wheel

L'evento wheel è uno standard di livello DOM3, ma oltre a questo evento, ci sono molti eventi non standard, e diversi browser utilizzano standard diversi, quindi potrebbe essere necessario adattarsi alle circostanze specifiche, consultare MDN MouseWheelEvent per ulteriori dettagli.

Giudizio in tempo reale

Se trovi fastidioso il salto di wheel e le varie compatibilità, ci sono ancora altre strade da percorrere, che sono ancora eventi scroll, ma che richiedono un po' di lavoro in più.

Il problema dell'evento scroll è che non c'è una判断当前哪个容器元素正在滚动,ma una volta determinato il contenitore di scorrimento attivo, tutto diventa più semplice, ad esempio, nell'uso dell'evento wheel, è possibile utilizzare l'evento scroll perché è facile determinare quale contenitore di scorrimento attivo è.

Quindi, la chiave del problema è come determinare quale contenitore di scorrimento è attivo. Una volta risolto questo problema, tutto il resto diventa più semplice.

Sia che si tratti di scorrimento della rotella del mouse o di trascinamento della barra di scorrimento, entrambi scatenano l'evento scroll e in questo momento, la coordinate del mouse lungo l'asse Z è sicuramente all'interno dell'area coperta dall'elemento contenitore di scorrimento, il che significa che il mouse è sospeso o si trova sopra l'elemento contenitore di scorrimento.

Quando il mouse si muove sulla schermata, è possibile ottenere le coordinate correnti del mouse.

In questo esempio, clientX e clientY rappresentano le coordinate correnti del mouse rispetto al viewport. Si può considerare che se questa coordinate si trova all'interno dell'area di un contenitore di scorrimento, allora questo elemento è considerato il contenitore di scorrimento attivo. L'area delle coordinate dell'elemento può essere ottenuta utilizzando getBoundingClientRect.

Di seguito è riportato un esempio di codice quando il mouse si muove sull'elemento .left:

if (e.clientX>l.left && e.clientX<l.right && e.clientY>l.top) {
  // Entrare nell'elemento .left
}

In realtà, questo funziona, ma dato che due elementi contenitori di scorrimento occupano quasi tutta l'area dello schermo, l'area che deve essere ascoltata per mousemove è abbastanza grande, il che potrebbe richiedere prestazioni elevate. Quindi, è possibile sostituire l'evento mouseover, che richiede solo di monitorare se il mouse entra in un elemento contenitore di scorrimento, evitando anche di determinare le coordinate.

addEventListener('mouseover',function(){
 // 进入 .left滚动容器元素内
)

当确定了鼠标主动滚动的容器元素是哪一个时,只需要处理这个容器的滚动事件,另外一个跟随滚动容器的滚动事件不做处理即可。

嗯,效果很不错,性能也很好,perfect,可以收工喽~

按比例滚动

上述示例全部是在两个滚动容器元素的内容高度完全一致的情况下的效果,如果这两个滚动容器元素的内容高度不同呢?

那就是下面这种效果:

可见,由于两个滚动容器元素的内容高度不同,所以最大的scrollTop也就不同,就会出现当其中一个scrollTop值较小的元素滚到底时,另外一个元素还停留在一半,或者当其中一个scrollTop值较大的元素才滚到一半时,另外一个元素就已经滚到底了。

这种情况很常见,例如你用markdown写作时,一个一级标题标记#在编辑模式下占用的高度,一般都是小于预览模式占用的高度的,这样就出现了左右两侧滚动高度不一致的情况。

所以,如果将这种情况也考虑进来的话,那么就不能简单地为两个滚动容器元素相互设置scrollTop值那么简单。

虽然无法固定住滚动容器内容的高度,但是有一点可以确定,滚动条最大滚动高度,或者说scrollTop的值,肯定是与滚动容器内容的高度与滚动容器本身的高度呈一定的关系。

由于需要知道滚动容器内容的高度,还要存在滚动条,所以需要给此容器元素加个子元素,子元素高度不限,就是滚动容器内容的高度,容器高度固定,溢出滚动即可。

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

结构示例如下:

通过我的观察推论与实践验证,已经确定下来了它们之间的关系,很简单,就是最基本的加减法运算:

滚动条的最大滚动高度(scrollTopMax) = 滚动容器内容的高度(即子元素高度ch) - 滚动容器本身的高度(即容器元素高度ph)

也就是说,如果已经确定了滚动容器内容的高度(即子元素高度ch)与滚动容器本身的高度(即容器元素高度ph),那么就一定能确定滚动条的最大滚动高度(scrollTop),而这两个高度值基本上都是可以获取到的,所以就能得到scrollTop

Quindi, per far sì che due elementi di contenitore di scorrimento si muovano in proporzione in modo simmetrico, ossia quando uno degli elementi raggiunge la fine o il fondo, l'altro elemento raggiunge anche la fine o il fondo corrispondente, è sufficiente ottenere la proporzione del valore massimo di scrollTop tra i due contenitori di scorrimento.

Dopo aver determinato lo scale, durante lo scorrimento in tempo reale, è sufficiente ottenere lo scrollTop1 dell'elemento di contenitore di scorrimento attivo per ottenere lo scrollTop2 dell'altro contenitore di scorrimento correlato:

Una volta chiarito il思路, scrivere il codice diventa molto facile, l'effetto è come segue:

Molto fluido~

Conclusione

L'implementazione sopra descritta ha soddisfatto le esigenze, ma potrebbe essere necessario apportare alcune modifiche in base alla situazione reale durante la pratica, ad esempio, se stai scrivendo una pagina di编辑 e anteprima online markdown, dovrai aggiornare il valore di scale in tempo reale in base alla altezza del contenuto inserito. Tuttavia, il lavoro principale è già stato completato e le modifiche minime non rappresentano una difficoltà.

Inoltre, ciò che è stato descritto in questo articolo non riguarda solo la seguente scorrimento sincronizzato di due contenitori di scorrimento, ma può essere esteso ulteriormente. La sincronizzazione della scorrimento tra più elementi può essere realizzata secondo l'idea descritta in questo articolo. Questo articolo si è concentrato sui due elementi specifici solo per facilitare l'insegnamento.

Sommario

Come già descritto, l'autore ha introdotto un'effetto di sincronizzazione della scorrimento di più barre di scorrimento native JS, sperando che sia utile ai lettori. Se avete domande, lasciate un commento e l'autore risponderà tempestivamente. Inoltre, l'autore desidera ringraziare tutti i sostenitori del tutorial di urla!

Dichiarazione: il contenuto di questo articolo è stato raccolto da Internet, il copyright spetta agli autori originali, il contenuto è stato contribuito e caricato autonomamente dagli utenti di Internet, questo sito non detiene i diritti di proprietà, non è stato elaborato manualmente e non assume alcuna responsabilità legale. Se trovi contenuti sospetti di violazione del copyright, ti preghiamo di inviare una email a: notice#oldtoolbag.com (al momento dell'invio dell'email, sostituisci # con @) per segnalare il problema e fornire prove pertinenti. Una volta verificata, questo sito eliminerà immediatamente i contenuti sospetti di violazione del copyright.

Ti potrebbe interessare