Пользовательская логика горизонтальной полосы прокрутки

Я сделал собственный скроллер div на основе easings TweenMax и событий колесика мыши jQuery. Прокрутка работает как шарм, но мне нужно переместить обработчик полосы прокрутки, чтобы показать ход прокрутки. Я сделал несколько тестов на основе вычислений родительской ширины, scrollLeft и общей ширины div, но не нашел правильного решения…
Я попытался адаптировать этот пример для моей горизонтальной полосы прокрутки, но это все еще странное поведение.
Есть идеи?

$(window).on("mousewheel DOMMouseScroll", function (e) {
    e.preventDefault();
    var delta = e.originalEvent.wheelDelta / 120 /* Chrome */ || -e.originalEvent.detail; /* FF */
    var dur = 1.2;
    TweenLite.to($("#photos"), dur, {
        scrollLeft: $("#photos").scrollLeft() - parseInt(delta * 35),
        ease: Expo.easeOut,
        overwrite: 5
    });
});

Живой пример:
http://jsfiddle.net/tccsq6e9/2/


person flks    schedule 25.01.2015    source источник
comment
Ваш handle имеет ширину 12%. Зачем? Ширина должна соответствовать правильному соотношению, а также должна рассчитываться при изменении размера окна, поскольку изменяется ширина прокрутки #photos.   -  person Roko C. Buljan    schedule 25.01.2015
comment
@RokoC.Buljan абсолютно нет, 12% - это всего лишь выбор стиля. Я просто хочу переместить этот обработчик полосы прокрутки.   -  person flks    schedule 25.01.2015
comment
О, понял, так что он всегда будет исправлен на 12% ширины. В любом случае, при изменении размера окна вам нужно будет пересчитать ширину обработчика и коэффициент ширины прокрутки области, чтобы он всегда достигал дальнего конца полосы прокрутки, когда #photos доходит до дальнего конца прокрутки.   -  person Roko C. Buljan    schedule 25.01.2015
comment
@ RokoC.Buljan, вот и все! / Не сохранил. Это не работало и очень глючило… :/   -  person flks    schedule 25.01.2015
comment
@RokoC.Buljan получил его из резервной копии Time Machine: jsfiddle.net/tccsq6e9/3 — как вы можете видеть, это много вычислений и нет ограничений на ребра. Я нашел этот код в онлайн-примере для вертикального полоса прокрутки.   -  person flks    schedule 25.01.2015


Ответы (1)


демонстрация jsBin

(PS: используйте отн. ="nofollow noreferrer">onUpdate функция обратного вызова для перемещения обработчика при прокрутке галереи)

Логика:

//     How much available scroll-space** have our elements:
//
//     |--$element--|<---------------avail scroll space--------------->|
//     |<------------------------total width-------------------------->|
//
//     Same logic goes for both elements:

galleryAvailScroll =  $gallery[0].scrollWidth - $gallery.outerWidth(true);
handlerAvailScroll =  $scrollbar.width() - $handler.width();

//     On $gallery scroll, how to move the $handler to the proper left position?

handlerLeft = $gallery.scrollLeft() * handlerAvailScroll / galleryAvailScroll; // left PX

Вот и все. Получите математику для прокрутки, а также для изменения размера окна, чтобы обработчик вел себя как настоящая полоса прокрутки:

Вот окончательный результат с упрощенным CSS, HTML и JS

var $gallery   = $("#thumbs"),
    $scrollbar = $("#scrollbar"),
    $handler   = $("#scrollbar-handle"),
    galleryAvailScroll = 0,
    handlerAvailScroll = 0,
    handlerLeft = 0;

function setPos() {
  galleryAvailScroll = $gallery[0].scrollWidth - $gallery.outerWidth(true); // PX
  handlerAvailScroll = $scrollbar.width() - $handler.width(); // PX
  handlerLeft = $gallery.scrollLeft() * handlerAvailScroll / galleryAvailScroll;
  $handler.css({left: handlerLeft});
}
setPos(); // Get the sizes
$(window).on("resize", setPos); // Also on resize

// Scroll
$gallery.on("mousewheel DOMMouseScroll", function (e) {
  e.preventDefault();
  var eorg  = e.originalEvent,
      delta = eorg.wheelDelta/120 || -eorg.detail; // Ch || FF
  
  TweenLite.to( $gallery, 0.7, {
    scrollLeft : $gallery.scrollLeft() - delta * 35,
    ease : Expo.easeOut,
    overwrite : 5,
    onUpdate : setPos
  });
  
});
body {
  padding: 15px 0 0;
  margin: 0;
}
#thumbs {
  position:relative;
  overflow:hidden;
  padding:0 10px;
  height: 175px;
  white-space: nowrap;
}
#thumbs > div {
  font-size:16px;
  display:inline-block;
  width: 150px; height: 150px;
  margin: 0 6px; /*6 + 4 LineWidth = 10 */
  background:#000;
}
#scrollbar {
  margin:0 70px;
  height:3px;
  position:relative;
  background:#ddd;
}
#scrollbar-handle {
  position:absolute;
  width:12%;
  left:0;
  height:3px;
  background:red;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="http://cdnjs.cloudflare.com/ajax/libs/gsap/latest/TweenMax.min.js"></script>
<div id="thumbs">
  <div></div>
  <div></div>
  <div></div>
  <div></div>
  <div></div>
  <div></div>
  <div></div>
  <div></div>
  <div></div>
  <div></div>
  <div></div>
  <div></div>
</div>

<div id="scrollbar">
  <span id="scrollbar-handle"></span>
</div>

Поскольку ваш обработчик «фиксирован» на ширине 12%, если вы решите сделать его ширину связанной с содержимым, вы можете получить необходимую математику из Как создать собственный плагин jQuery для полосы прокрутки

person Roko C. Buljan    schedule 26.01.2015
comment
Дэн, ты мужчина! Не знал, что можно использовать обратный вызов onUpdate для перемещения индикатора полосы прокрутки. Большое вам спасибо за вашу помощь :) - person flks; 26.01.2015
comment
Я только что адаптировал код к моему реальному дизайну, и он работает как шарм. Но у меня простой вопрос: почему когда я пишу onUpdate: setPos(), он работает, но двигается неровно? onUpdate: setPos работает хорошо. Рассматривается ли setPos() как функция, а setPos как переменная? Или что-то вроде того? - person flks; 26.01.2015
comment
@flks stackoverflow.com/questions/3246928/ - person Roko C. Buljan; 26.01.2015
comment
@flks (если ответы были немного запутанными) Метод onUpdate принимает функцию: onUpdate : function(){ /*code here*/ }, поэтому мы используем его как onUpdate : setPos, поэтому назначая этот метод (свойство)› наша функция. Если бы вместо этого вы написали onUpdate : setPos(), это было бы почти так же, как Немедленное выполнение этой функции (вместо назначения), например: onUpdate : (function(){ /*execute!*/ }()), что нам не нужно и для чего там сделан обратный вызов.. - person Roko C. Buljan; 26.01.2015
comment
Ок, понял. Спасибо за ваше объяснение! - person flks; 27.01.2015