1

I made a custom div scroller based on TweenMax easings and jQuery mousewheel events. The scroll works like a charm but I need to move the scrollbar handler to show the scroll progress. I made some tests based on calcs of parent width, scrollLeft and total div width, but didn't found the right solution…
I tried to adapt this example for my horizontal scrollbar, but it's still a strange behaviour.
Any idea?

$(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
    });
});

Live example:
http://jsfiddle.net/tccsq6e9/2/

Roko C. Buljan
  • 196,159
  • 39
  • 305
  • 313
flks
  • 610
  • 10
  • 28
  • Your `handle` has width 12%. why? The width should match the right ratio and should be also calculated on window resize since than the scrollWidth of the #photos changes. – Roko C. Buljan Jan 25 '15 at 20:02
  • @RokoC.Buljan absolutely not, the 12% is only a styling choice. I just want to move that scrollbar handler. – flks Jan 25 '15 at 20:04
  • Ohh, got it so it'll always be "fixed" at 12% width. Any way, on window resize you'll need to re-calculate the handler width and the area scrollwidth ratio to make it always arrive at the far scrollbar-end when the #photos gets to it's far-scroll-end. – Roko C. Buljan Jan 25 '15 at 20:14
  • @RokoC.Buljan that's it! / Didn't kept. That was not working and very buggy… :/ – flks Jan 25 '15 at 20:25
  • @RokoC.Buljan got it back from a Time Machine backup: http://jsfiddle.net/tccsq6e9/3/ — as you can see, that's a lot of calcs and there is no limit on edges. I found that code on an [online example](http://istockphp.com/demo/how-to-create-jquery-scrollbar-div/) for a vertical scrollbar. – flks Jan 25 '15 at 20:53

1 Answers1

2

Scrollbars math logic

Scrollbars logic:

(onInit, onResize) thumbSize = viewportSize * viewportSize / areaSize
(onThumbDrag)      viewportScrollPos = areaSize / viewportSize * thumbPos
(onViewportScroll) thumbPos = viewportScrollPos / viewportSize * thumbSize

That's it. Get the math on scroll but also on window resize to make the handler act like a real scrollbar would:

Here's the final result with simplified CSS, HTML and 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>

Since your handler is "fixed" at 12% width, if you decide to make it's width content-related, you can get the needed math from How to make custom scrollbar jQuery plugin

Roko C. Buljan
  • 196,159
  • 39
  • 305
  • 313
  • 1
    Dang, you're the man! Didn't know that was possible to use that onUpdate callback to move the scrollbar indicator. Thank you so much for your help :) – flks Jan 26 '15 at 09:16
  • I just adapted the code in my actual design, that works like a charm. But I have a simple question: why when I write `onUpdate: setPos()`, it works but move bumpy? `onUpdate: setPos` works well. Is `setPos()` considered as a function and `setPos` as a variable? Or something like that? – flks Jan 26 '15 at 19:02
  • @flks http://stackoverflow.com/questions/3246928/in-javascript-does-it-make-a-difference-if-i-call-a-function-with-parentheses – Roko C. Buljan Jan 26 '15 at 20:11
  • @flks (if the answers there were a bit confusing) The Method `onUpdate` accepts a **function**: `onUpdate : function(){ /*code here*/ }`, so we use it like `onUpdate : setPos` so by *Assigning* that Method (property)> our function. If instead you wrote `onUpdate : setPos()` would be pretty much the same as *Executing immediately that function* (instead of assigning) like: `onUpdate : (function(){ /*execute!*/ }())` which is not what we need and what the callback is there made for.. – Roko C. Buljan Jan 26 '15 at 20:35