Механизм системного скролла реализуется на уровне базовых возможностей операционной системы, поэтому с уверенностью можно сказать, что он всегда лучше js-эмуляции: он производительнее, работает независимо от JavaScript и реализует все необходимые «фичи» системы для разного типа устройств.
Дизайн системного скроллбара способен изуродовать значительную часть сайтов интернета.
Доброго времени суток!
Хочу немножко покопаться в окрестностях данной темы.
На момент написания статьи, более или менее гибко кастомизировать скроллбар средствами CSS можно только в браузерах на движке webkit.
Сразу пример:
Пропишем несколько строк и откроем страницу в том же chrome:
// SCROLL
::-webkit-scrollbar {
width: 10px;
height: 10px;
}
::-webkit-scrollbar-track {
-webkit-box-shadow: inset 0 0 6px rgba(0,0,0,0.3);
/*border-radius: 3px;*/
}
::-webkit-scrollbar-thumb {
// border-radius: 10px;
-webkit-box-shadow: inset 0 0 6px rgba(0,0,0,0.5);
}
В остальном это попытки большого числа js-библиотек, более половины которых подменяют нативный механизм скролла. При таком подходе возникает сразу два фундаментальных недостатка: отсутствие кроссбраузерности и отсутствие же кроссплатформенности.
Когда-то я использовал jScrollPane. На тот момент плагин вполне справлялся со своей задачей, но сейчас понимаю, что на странице генерируется куча мусора, которую сложно контролировать.
Вообще, все подобные решения - УЖаС, по этому лучше закройте эту статью и призывайте всех перейти на лучший браузер в мире.
Но все таки давайте поразмышляем немножко на эту тему..
Поставим задачу (а это пол дела!):
Скрыть системный скроллбар
Отобразить кастомный скроллбар.
Все должно быть максимально просто и понятно, а подключение не требует дополнительных махинаций
В особенности хочу обратить внимание на третий пункт. Решения, что требуют для себя слишком многое - в пекло. Есть архитектура приложения, и ничто не должно на нее сильно влиять. Иначе - будем грести проблемы с поддержкой в будущем.
Например, раньше, чтобы поставить Яндекс карты, нужно было написать целое сочинение. Сейчас они все упаковали в одну строчку.
Я опишу идею, которая может быть будет кому-то полезна в велосипедостроении :)
HTML
-
Lorem ipsum dolor sit amet, consectetur adipisicing elit
Lorem ipsum dolor sit amet, consectetur adipisicing elit
Lorem ipsum dolor sit amet, consectetur adipisicing elit
-
Lorem ipsum dolor sit amet, consectetur adipisicing elit
Lorem ipsum dolor sit amet, consectetur adipisicing elit
Lorem ipsum dolor sit amet, consectetur adipisicing elit
LESS
.t-scroll-container {
overflow: hidden !important; /* Прячем прокрутку */
position: relative;
& > .t-scroll { /* Полоса, по которой будет бегать наш ползунок */
position: absolute;
right: 0px;
top: 0;
width: 12px;
background: rgb(243, 247, 254);
z-index: 2;
button { /* Ползунок, собственно :) */
position: absolute;
right: 0;
top: 0;
width: 10px;
height: 50px;
background: #b6b6b6;
border-radius: 4px;
}
}
}
JS
var makeThis = function(el) {
if (!el.length)
return false;
var el = $(el[0]);
if (el.hasClass('t-scroll-container')) // Если мы уже тут были, уходим
return false;
// Формируем наш ползунок. Все просто: div, внутри button
var scroll = $('<div class="t-scroll"/>').append('<button type="button"/>');
el.append(scroll);
el.addClass('t-scroll-container');
// Update
var change = function() {
// Если скролл не нужен - прячем
if (!(el[0].scrollHeight - el.outerHeight()))
el.find(' > .t-scroll').hide();
// Задаем высоту полоски под ползунком
el.find(' > .t-scroll').css('height', el[0].scrollHeight);
// Высчитываем высоту ползунка.
// Как вариант - можно задать фиксированное значение или переписать
var butH = el.outerHeight() / 6;
var scrollDelta = (el[0].scrollHeight - el.outerHeight());
if (el.outerHeight() < scrollDelta)
butH = butH / (scrollDelta / el.outerHeight());
butH = Math.max(butH, 30);
el.find(' > .t-scroll button').css('height', butH);
}
change();
// Запоминаем смещение скролла и
// расстояние между точкой клика на кнопку и верзней грани ползунка
el[0].bScroll = {
delta: Math.min(el.outerHeight() / 3,
(el[0].scrollHeight - el.outerHeight()) / 4),
offsetY: 0
}
//
var mouseUp = function(e) {
window.removeEventListener('mousemove', mouseMove);
}
//
var mouseMove = function(e) {
if (!el[0].bScroll)
return false;
// Получили высоту кнопки
var butH = el.find(' > .t-scroll button').outerHeight();
// Считаем разницу
var diff = -(el.offset().top - $(window).scrollTop() - e.clientY) -
el[0].bScroll.offsetY;
// Находим процент прокрутки / 100
h = diff / (el.outerHeight() - butH);
h = Math.max(h, 0);
h = Math.min(h, 1);
// Новую позицию
var fScrollTop = h * (el[0].scrollHeight - el.outerHeight());
// Двигаем документ
el.scrollTop(fScrollTop);
// Находим позицию ползунка
var scrollPos = fScrollTop + h * (el.outerHeight() - butH);
// Двигаем
el.find('.t-scroll button').css('top', scrollPos);
return false;
}
//
var scrollTo = function(fScrollTop) {
// Ограничения
fScrollTop = Math.max(fScrollTop, 0);
fScrollTop = Math.min(fScrollTop, el[0].scrollHeight - el.outerHeight());
// Защита от деления на 0
if (!(el[0].scrollHeight - el.outerHeight()))
return false;
// Ищем позицию ползунка
var h = fScrollTop / (el[0].scrollHeight - el.outerHeight());
var pos = fScrollTop + el.outerHeight() * h;
pos -= el.find('.t-scroll button').outerHeight() * h;
// Двигаем
el.find('.t-scroll button').stop().animate({
top: pos,
}, 'fast');
el.stop().animate({
scrollTop: fScrollTop,
}, 'fast');
}
// EVENTS
el.on('DOMNodeInserted resize', function(e) {
change();
scrollTo(el.scrollTop());
});
// MOUSE WHEEL
el.on('DOMMouseScroll mousewheel', function(e) {
if (!el[0].bScroll)
return false;
var fScrollTop = el.scrollTop() -
Math.sign(e.originalEvent.wheelDelta || -e.originalEvent.detail) *
el[0].bScroll.delta
scrollTo(fScrollTop);
return false;
});
// MOUSEDOWN
el.find(' > .t-scroll button').off('mousedown');
el.find(' > .t-scroll button').on('mousedown', function(e) {
if (!el[0].bScroll)
return false;
el[0].bScroll.offsetY = e.offsetY;
window.addEventListener('mouseup', mouseUp);
window.addEventListener('mousemove', mouseMove);
});
}
Все, что нам осталось, это вызвать makeThis для нашего ul
$(document).ready(function() {
makeThis($('.it-is-me'));
});
Обработку touchstart, touchmove, touchup можно посмотреть в коде примера ниже или написать самостоятельно по аналогии :)