Поскольку я недавно пишу UI-библиотеку проекта, я долгое время застревал, когда сталкивался с порогом пользовательских полос прокрутки, в основном о том, как автоматически отслеживать изменения содержимого и обновлять высоту полосы прокрутки. В основном все плагины полосы прокрутки на рынке не реализовали это, и, наконец, исходный код элемента наконец-то решен. Об этом в основном и говорится в этой статье.
Прежде всего, мы сначала определяем функции, которые необходимо реализовать.
- Щелкните левой кнопкой мыши, чтобы перетащить
- прокрутка колеса мыши
- При изменении содержимого длина полосы прокрутки автоматически обновляется.
- Предоставьте разработчикам интерфейс для прокрутки обратных вызовов
Полагаться на родную полосу прокрутки для первых двух пунктов на самом деле относительно просто, а вот третий пункт действительно застрял у меня надолго, и я не придумал его после долгого раздумья. В конце концов, только после прочтения исходного кода элемента все получилось.
Далее я возьму вертикальную полосу прокрутки в качестве примера (горизонтальная полоса прокрутки в основном такая же) и реализую пользовательскую полосу прокрутки. Я стараюсь объяснить детали принципа ясно.
1. Создайте базовую структуру стиля
Начнем с написания HTML и стилей
<div class="scrollbar">
<div class="scrollbar-content">
<ul class="box">
<li>11111</li><li>11111</li><li>11111</li><li>11111</li>
<li>11111</li><li>11111</li><li>11111</li><li>11111</li>
<li>11111</li><li>11111</li><li>11111</li><li>11111</li>
<li>11111</li><li>11111</li><li>11111</li><li>11111</li>
</ul>
</div>
<div class='scrollbar-bar'>
<div ref="thumb" class="scrollbar-thumb"></div>
</div>
</div>
Рамка полосы прокрутки показана выше, а во второй половине дня я буду ссылаться на аббревиатуруwrap
,bar
,thumb
сокращать
-
wrap
: Обтекатель области содержимого -
bar
: Поле прокрутки для пользовательской полосы прокрутки в области переноса. -
thumb
: пользовательская полоса прокрутки
Прежде чем мы начнем, каждый должен помнить одну вещь: мы не используем собственные полосы прокрутки, фактически все наши операции должны полагаться на собственные полосы прокрутки для достижения. Просто он скрыт в темноте, а настраиваемая полоса прокрутки, которая делает пользовательский интерфейс лучше, появляется на свету.
1.1 Рассчитайте ширину полосы прокрутки.
Первый шаг — скрыть родную полосу прокрутки. Но здесь задействована первая проблема, то есть ширина полосы прокрутки разных браузеровРазныеиз. Нам нужно точно знать, еслиwrap
Создается полоса прокрутки, какова ее ширина.
Сначала напишите функцию обратного вызова, которая получает ширину полосы прокрутки (scrollWidth) в областиgetScrollWidth
, Получив высоту полосы прокрутки,
function getScrollWidth(){
const outer = document.createElement("div");
outer.className = "el-scrollbar__wrap";
outer.style.width = '100px';
outer.style.visibility = "hidden";
outer.style.position = "absolute";
outer.style.top = "-9999px";
document.body.appendChild(outer);
const widthNoScroll = outer.offsetWidth;
outer.style.overflow = "scroll";
const inner = document.createElement("div");
inner.style.width = "100%";
outer.appendChild(inner);
const widthWithScroll = inner.offsetWidth;
outer.parentNode.removeChild(outer);
scrollBarWidth = widthNoScroll - widthWithScroll;
return scrollBarWidth;
}
Получить ширину полосы прокруткиscrollBarWidth
После этого установитеwrap
css стили, черезmarginRight
Уберите полосу прокрутки из поля зрения
wrap.style.overflow = scroll;
wrap.style.marginRight = -scrollWidth + "px";
1.2 Рассчитайте высоту полосы прокрутки.
Вторым шагом нам нужно рассчитать высоту полосы прокрутки. Метод расчета также очень прост, высота элементаscrollHieght
/высота содержимогоclientHeight
, который представляет собой процент полос прокрутки.
Поскольку высота содержимого часто меняется, мы можем написать функцию обратного вызова, которая обновляет высоту полосы прокрутки.updateThumb
, который удобно вызывать позже в любое время.
function updateThumb(){
let heightPercentage = (wrap.clientHeight * 100 / wrap.scrollHeight);
thumb.style.height = heightPercentage + "%";
}
На данный момент в основном появился основной стиль полосы прокрутки. Далее мы хотим реализовать его функцию использования.
Посмотреть результаты первого шага
2. Добавьте функцию скольжения полосы прокрутки
На данный момент мы уже можем видеть UI-интерфейс сформированной полосы прокрутки, но функции прокрутки и перетаскивания по-прежнему отсутствуют. Ключевым моментом является то, как отслеживать изменения полосы прокрутки.
2.1 Роликовое скольжение
Помните, что мы говорили в начале статьи, реализация всех наших функций опирается на скрытые нативные полосы прокрутки. Если вы понимаете, что я сказал выше, то проблема проста.Когда мы начинаем двигать колесо прокрутки, в то же время будет прокручиваться скрытая в темноте собственная полоса прокрутки, и в это время будет срабатывать событие прокрутки собственной полосы прокрутки..
Более подробно это можно объяснить здесь. Пока scrollTop элемента изменяется, событие прокрутки обязательно будет запущено. Таким образом, мы используем колесо прокрутки, которое существенно меняет scrollTop элемента.
Так что нам просто нужно написать соответствующую функцию обратного вызоваhandleScroll
, просто изменяйте стиль нашей пользовательской полосы прокрутки в режиме реального времени каждый раз, когда запускается обратный вызов.
function handleScroll(){
this.moveY = (wrap.scrollTop *100 / wrap.clientHeight);
//通过计算出来的百分比,然后对滚动条执行translate移动
thumb.style.transform = "translateY" + moveY;
},
wrap.addEventListener('scroll',handleScroll);
Проверьте эффект колеса прокрутки
2.2 Щелкните поле прокрутки, полоса прокрутки и содержимое переместятся в соответствующую позицию.
Далее реализуем вторую функцию. Когда мы нажимаем на позицию полосы прокрутки, полоса прокрутки также переместится в эту позицию, и положение содержимого также изменится.
Первым шагом является получение координаты y щелчка, а затем вычисление и滚动框bar
Расстояние от вершины, а затем вычислить процент полосы прокрутки, который является высотой полосы прокрутки
function clickTrackHandle(e){
//获得点击位置与滚动框顶部之间的距离
const offset = Math.abs(e.target.getBoundingClientRect().top - e.clientY)
//让点击位置处于滚动条的中间
const thumbHalf = thumb.offsetHeight / 2;
//计算出滚动条在滚动框的百分比位置
const thumbPositionPercentage = (offset - thumbHalf) * 100 / wrap.offsetHeight;
//通过改变scrollTop来操作。所有操作滚动条的最后一步都是通过handleScroll来实现
wrap.scrollTop = (thumbPositionPercentage * wrap.scrollHeight / 100);
}
bar.addEventListener("click",clickTrackHandle);
Пока значение scrollTop изменяется, будет запущен обратный вызов, который мы написали на предыдущем шаге.
Посмотрите, как щелкнет ползунок прокрутки.
2.3 Перетащите полосу прокрутки, чтобы переместить содержимое
Затем мы будем вручную перетаскивать полосу прокрутки, чтобы реализовать движущееся содержимое.Эта точка знаний является точкой знаний перетаскивания, но при просмотре исходного кода мы обнаружили, чтоelement
Привычка очень хорошая, он привязывает перетаскивание при нажатии на полосу прокрутки, а потом отвязывает при отпускании.
function mouseMoveDocumentHandler(){}; //实时记录滚动条位置的拖拽函数
//当点击滚动条时
document.addEventListener("mousedown",mouseMoveDocumentHandler);
document.onselectstart = false; //同时阻止选中
//当松开滚动条时
document.removeEventListener("mousedown",mouseMoveDocumentHandler);
document.onselectstart = null; //同时阻止选中
В связи с тем, что кода в этом куске много, он не будет размещен в статье, его можно прочитать по прямой ссылке.
Просмотр эффекта перетаскивания полосы прокрутки
3. Обновление полосы прокрутки в режиме реального времени с содержимым
Вторая глава в основном посвящена реализации функции полосы прокрутки, эта глава посвящена запутыванию. 😖 Моя фишка на долгое время.
Поскольку высота полосы прокрутки не определяется в начале, ее необходимо определить после рендеринга содержимого dom.А иногда по мере изменения содержимого также необходимо менять высоту полосы прокрутки в режиме реального времени.. Посмотрев полосы прокрутки на рынке, я обнаружил, что они в принципе не отвечают этой функции.
На самом деле, без этого визуального взаимодействия не будет. Например, добавление элемента, изначально имевшего полосу прокрутки, привело к тому, что полоса прокрутки стала маленькой, потому что содержимое было уменьшено, но настраиваемая полоса прокрутки все еще существовала, потому что не было обнаружено никаких изменений, что могло бы ввести пользователя в замешательство.
Я не хочу обновлять полосу прокрутки, добавляя функцию обратного вызова каждый раз, когда содержимое обновляется, но я хочу, чтобы она обновлялась в реальном времени сама по себе. После того, как я не смог найти ответ в Интернете, я, наконец, просмотрел исходный код элемента, долго изучал его и, наконец, нашел ответ, который хотел.
Ключевым моментом является то, что я могу произнести предложение перед -Если мы изменим scrollTop элемента, будет запущено событие прокрутки.
Каждый представляет себе сценарий, если полоса прокрутки всегда появляется внизу, как на картинке ниже
Поэтому, пока мой контент немного меняется, полоса прокрутки неизбежно становится длиннее или короче. Затем, когда длина полосы прокрутки изменяется, scrollTop естественным образом изменяется (scrollTop становится равным 0, когда полоса прокрутки исчезает), затем срабатывает функция обратного вызова прокрутки, и тогда мы автоматически обнаружим ее. 😊.
После осознания этого возник другой вопрос. В обычных условиях полоса прокрутки не может отображаться внизу, что мне делать?
element
Я решил построить полосу прокрутки внизу, чтобы удовлетворить мои потребности.
Сделал демо, проверьте эффект, нажмите здесь
<script>
const ul = document.getElementById("ul");
const resizeTrigger = document.createElement("div");
resizeTrigger.className = "resize-triggers";
resizeTrigger.innerHTML = '<div class="expand-trigger"><div><div></div></div></div>';
ul.appendChild(resizeTrigger);
const resetTrigger = function (element) {
const trigger = element.__resizeTrigger__;
const expand = trigger.firstElementChild;
const expandChild = expand.firstElementChild;
expandChild.style.height = expand.offsetHeight + 1 + 'px';
expand.scrollTop = expand.scrollHeight;
};
ul.addEventListener("scroll",function(){
resetTrigger(this);
},true)
</script>
ul
это элемент DOM, в который мы оборачиваем содержимое.
В сочетании с css мы создали первый пункт JSresizeTrigger
этот div, и мы помещаем егоheight:100%
. Таким образом, если содержимое изменится,resizeTrigger
навсегда и родительский элементul
Также изменить высоту.Здесь очень важно установить высоту 100%, чтобы он мог активно синхронизироваться с изменениями контента..
уведомлениеresizeTrigger
В нем также есть элемент «родитель-потомок».expand
а такжеexpandChild
. Во втором абзаце JSresetTrigger
в функции. затем установитеexpandChild
высота превышает родительский элементexpand
высота, подсказкаexpand
Создание полос прокрутки. Затем мы помещаем полосу прокруткиscrollTop
Установите на максимум, чтобы вспомогательная полоса прокрутки отображалась в области прокрутки.resizeTrigger
нижняя часть.
Теперь мы установили полосу прокрутки внизу, поэтому, пока содержимое изменяется, scrollTop полосы прокрутки также должен измениться..
Последний фрагмент кода — монитор прокрутки. при прослушиванииscrollTop
При изменении значения запускается соответствующая функция обратного вызова.
Итак, окончательная логика этого кода на самом деле такова. Изменение содержания -->ul
изменение высоты -->resizeTrigger
изменение высоты -->expand
полоса прокруткиscrollTop
Изменить --> запустить функцию обратного вызова прокрутки, снова отрегулируйте высоту полосы прокрутки в функции, чтобы убедиться, что высота полосы прокрутки правильная.
С помощью этих трех фрагментов кода мы в основном реализовали автоматический мониторинг изменений содержимого для обновления полосы прокрутки.
Нарисована простая схема, помогающая понять логику
Полосы прокрутки, созданные двумя маленькими синими прямоугольниками, помогают отслеживать изменения содержимого.
4. Реализовать компонентизацию, которую удобно использовать разработчикам
После трех вышеперечисленных шагов пользовательская полоса прокрутки в основном может быть реализована. Приведенный выше код предназначен для нативного js. В нашем проекте четвертый пункт достигается путем инкапсуляции компонента полосы прокрутки и его использования в проекте.
Это требование отличается для разных фреймворков, поэтому я не буду подробно выкладывать код, потому что мой проект использует фреймворк Vue, так что это компонент Vue, и вы можете сами его увидеть, если вам это нужно.
Просмотр компонента полосы прокрутки
Что ж, это конец статьи, и я многому научился в процессе просмотра исходного кода других людей. Например, использование JSX для написания компонентов; мониторинг прокрутки фактически оценивает scrollTop; например, мониторинг scrollTop путем самостоятельного создания полос прокрутки для достижения автоматических обновлений. Наконец, благодаря написанию статей я углубил свое понимание некоторых новых знаний.