Team:NCTU Formosa/slimscroll.js
/*! Copyright (c) 2011 Piotr Rochala (http://rocha.la)
* Dual licensed under the MIT (http://www.opensource.org/licenses/mit-license.php) * and GPL (http://www.opensource.org/licenses/gpl-license.php) licenses. * * Version: 1.3.2 (modified for fullpage.js) * */
(function($) {
jQuery.fn.extend({ slimScroll: function(options) {
var defaults = {
// width in pixels of the visible scroll area width : 'auto',
// height in pixels of the visible scroll area height : '250px',
// width in pixels of the scrollbar and rail size : '7px',
// scrollbar color, accepts any hex/color value color: '#000',
// scrollbar position - left/right position : 'right',
// distance in pixels between the side edge and the scrollbar distance : '1px',
// default scroll position on load - top / bottom / $('selector') start : 'top',
// sets scrollbar opacity opacity : .4,
// enables always-on mode for the scrollbar alwaysVisible : false,
// check if we should hide the scrollbar when user is hovering over disableFadeOut : false,
// sets visibility of the rail railVisible : false,
// sets rail color railColor : '#333',
// sets rail opacity railOpacity : .2,
// whether we should use jQuery UI Draggable to enable bar dragging railDraggable : true,
// defautlt CSS class of the slimscroll rail railClass : 'slimScrollRail',
// defautlt CSS class of the slimscroll bar barClass : 'slimScrollBar',
// defautlt CSS class of the slimscroll wrapper wrapperClass : 'slimScrollDiv',
// check if mousewheel should scroll the window if we reach top/bottom allowPageScroll : false,
// scroll amount applied to each mouse wheel step wheelStep : 20,
// scroll amount applied when user is using gestures touchScrollStep : 200,
// sets border radius borderRadius: '7px',
// sets border radius of the rail railBorderRadius : '7px' };
var o = $.extend(defaults, options);
// do it for every element that matches selector this.each(function(){
var isOverPanel, isOverBar, isDragg, queueHide, touchDif, barHeight, percentScroll, lastScroll,divS = '',
minBarHeight = 30, releaseScroll = false;
// used in event handlers and for better minification var me = $(this);
// ensure we are not binding it again if (me.parent().hasClass(o.wrapperClass)) { // start from last bar position var offset = me.scrollTop();
// find bar and rail bar = me.parent().find('.' + o.barClass); rail = me.parent().find('.' + o.railClass);
getBarHeight();
// check if we should scroll existing instance if ($.isPlainObject(options)) { // Pass height: auto to an existing slimscroll object to force a resize after contents have changed if ( 'height' in options && options.height == 'auto' ) { me.parent().css('height', 'auto'); me.css('height', 'auto'); var height = me.parent().parent().height(); me.parent().css('height', height); me.css('height', height); }
if ('scrollTo' in options) { // jump to a static point offset = parseInt(o.scrollTo); } else if ('scrollBy' in options) { // jump by value pixels offset += parseInt(o.scrollBy); } else if ('destroy' in options) { // clear slimscroll mouse event listeners detachWheel();
// remove slimscroll elements bar.remove(); rail.remove(); me.unwrap(); return; }
// scroll content by the given offset scrollContent(offset, false, true); }
return; }
// optionally set height to the parent's height o.height = (options.height == 'auto') ? me.parent().height() : options.height;
// wrap content var wrapper = $(divS) .addClass(o.wrapperClass) .css({ position: 'relative', overflow: 'hidden', width: o.width, height: o.height });
// update style for the div me.css({ overflow: 'hidden', width: o.width, height: o.height });
// create scrollbar rail var rail = $(divS) .addClass(o.railClass) .css({ width: o.size, height: '100%', position: 'absolute', top: 0, display: (o.alwaysVisible && o.railVisible) ? 'block' : 'none', 'border-radius': o.railBorderRadius, background: o.railColor, opacity: o.railOpacity, zIndex: 90 });
// create scrollbar var bar = $(divS) .addClass(o.barClass) .css({ background: o.color, width: o.size, position: 'absolute', top: 0, opacity: o.opacity, display: o.alwaysVisible ? 'block' : 'none', 'border-radius' : o.borderRadius, BorderRadius: o.borderRadius, MozBorderRadius: o.borderRadius, WebkitBorderRadius: o.borderRadius, zIndex: 99 });
// set position var posCss = (o.position == 'right') ? { right: o.distance } : { left: o.distance }; rail.css(posCss); bar.css(posCss);
// wrap it me.wrap(wrapper);
// append to parent div me.parent().append(bar); me.parent().append(rail);
// make it draggable and no longer dependent on the jqueryUI if (o.railDraggable){ bar.bind("mousedown", function(e) { var $doc = $(document); isDragg = true; t = parseFloat(bar.css('top')); pageY = e.pageY;
$doc.bind("mousemove.slimscroll", function(e){ currTop = t + e.pageY - pageY; bar.css('top', currTop); scrollContent(0, bar.position().top, false);// scroll content });
$doc.bind("mouseup.slimscroll", function(e) { isDragg = false;hideBar(); $doc.unbind('.slimscroll'); }); return false; }).bind("selectstart.slimscroll", function(e){ e.stopPropagation(); e.preventDefault(); return false; }); }
// on rail over rail.hover(function(){ showBar(); }, function(){ hideBar(); });
// on bar over bar.hover(function(){ isOverBar = true; }, function(){ isOverBar = false; });
// show on parent mouseover me.hover(function(){ isOverPanel = true; showBar(); hideBar(); }, function(){ isOverPanel = false; hideBar(); });
// support for mobile me.bind('touchstart', function(e,b){ if (e.originalEvent.touches.length) { // record where touch started touchDif = e.originalEvent.touches[0].pageY; } });
me.bind('touchmove', function(e){ // prevent scrolling the page if necessary if(!releaseScroll) { e.originalEvent.preventDefault();
}
if (e.originalEvent.touches.length) { // see how far user swiped var diff = (touchDif - e.originalEvent.touches[0].pageY) / o.touchScrollStep; // scroll content scrollContent(diff, true); touchDif = e.originalEvent.touches[0].pageY; } });
// set up initial height getBarHeight();
// check start position if (o.start === 'bottom') { // scroll content to bottom bar.css({ top: me.outerHeight() - bar.outerHeight() }); scrollContent(0, true); } else if (o.start !== 'top') { // assume jQuery selector scrollContent($(o.start).position().top, null, true);
// make sure bar stays hidden if (!o.alwaysVisible) { bar.hide(); } }
// attach scroll events attachWheel();
function _onWheel(e) { // use mouse wheel only when mouse is over if (!isOverPanel) { return; }
var e = e || window.event;
var delta = 0; if (e.wheelDelta) { delta = -e.wheelDelta/120; } if (e.detail) { delta = e.detail / 3; }
var target = e.target || e.srcTarget || e.srcElement; if ($(target).closest('.' + o.wrapperClass).is(me.parent())) { // scroll content scrollContent(delta, true); }
// stop window scroll if (e.preventDefault && !releaseScroll) { e.preventDefault(); } if (!releaseScroll) { e.returnValue = false; } }
function scrollContent(y, isWheel, isJump) { releaseScroll = false; var delta = y; var maxTop = me.outerHeight() - bar.outerHeight();
if (isWheel) { // move bar with mouse wheel delta = parseInt(bar.css('top')) + y * parseInt(o.wheelStep) / 100 * bar.outerHeight();
// move bar, make sure it doesn't go out delta = Math.min(Math.max(delta, 0), maxTop);
// if scrolling down, make sure a fractional change to the // scroll position isn't rounded away when the scrollbar's CSS is set // this flooring of delta would happened automatically when // bar.css is set below, but we floor here for clarity delta = (y > 0) ? Math.ceil(delta) : Math.floor(delta);
// scroll the scrollbar bar.css({ top: delta + 'px' }); }
// calculate actual scroll amount percentScroll = parseInt(bar.css('top')) / (me.outerHeight() - bar.outerHeight()); delta = percentScroll * (me[0].scrollHeight - me.outerHeight());
if (isJump) { delta = y; var offsetTop = delta / me[0].scrollHeight * me.outerHeight(); offsetTop = Math.min(Math.max(offsetTop, 0), maxTop); bar.css({ top: offsetTop + 'px' }); }
// scroll content me.scrollTop(delta);
// fire scrolling event me.trigger('slimscrolling', ~~delta);
// ensure bar is visible showBar();
// trigger hide when scroll is stopped hideBar(); }
function attachWheel() { if (window.addEventListener) { this.addEventListener('DOMMouseScroll', _onWheel, false ); this.addEventListener('mousewheel', _onWheel, false ); } else { document.attachEvent("onmousewheel", _onWheel) } }
function detachWheel() { if (window.removeEventListener) { this.removeEventListener('DOMMouseScroll', _onWheel); this.removeEventListener('mousewheel', _onWheel); } else { document.detachEvent('onmousewheel', _onWheel); } }
function getBarHeight() { // calculate scrollbar height and make sure it is not too small barHeight = Math.max((me.outerHeight() / me[0].scrollHeight) * me.outerHeight(), minBarHeight); bar.css({ height: barHeight + 'px' });
// hide scrollbar if content is not long enough var display = barHeight == me.outerHeight() ? 'none' : 'block'; bar.css({ display: display }); }
function showBar() { // recalculate bar height getBarHeight(); clearTimeout(queueHide);
// when bar reached top or bottom if (percentScroll == ~~percentScroll) { //release wheel releaseScroll = o.allowPageScroll;
// publish approporiate event if (lastScroll != percentScroll) { var msg = (~~percentScroll == 0) ? 'top' : 'bottom'; me.trigger('slimscroll', msg); } } else { releaseScroll = false; } lastScroll = percentScroll;
// show only when required if(barHeight >= me.outerHeight()) { //allow window scroll releaseScroll = true; return; } bar.stop(true,true).fadeIn('fast'); if (o.railVisible) { rail.stop(true,true).fadeIn('fast'); } }
function hideBar() { // only hide when options allow it if (!o.alwaysVisible) { queueHide = setTimeout(function(){ if (!(o.disableFadeOut && isOverPanel) && !isOverBar && !isDragg) { bar.fadeOut('slow'); rail.fadeOut('slow'); } }, 1000); } }
});
// maintain chainability return this; } });
jQuery.fn.extend({ slimscroll: jQuery.fn.slimScroll });
})(jQuery);