Smoothscroll

Smooth scrolling on pages using javascript and jquery

当前为 2016-05-20 提交的版本,查看 最新版本

// ==UserScript==
// @name Smoothscroll
// @include     http*
// @author       Creec Winceptor
// @description  Smooth scrolling on pages using javascript and jquery
// @namespace https://greasyfork.org/users/3167
// @run-at document-idle
// @grant    GM_info
// @grant         unsafeWindow
// @version 0.0.1.20160520212705
// ==/UserScript==






//SETTINGS HERE

//Smoothness factor value (how strong the smoothing effect is)
//values: 1-(infinite) (default = 50)
var smoothness = 30;


//Scroll sensitivity
//values: anything? (default 1.00)
var sensitivity = 1.00;


//Acceleration sensitivity
//values: anything? (default 1.00)
var acceleration = 1.00;


//Refreshrate setting
//values: 30-144 (default = 60/72/120/144 = same as your monitor hz)
var baserefreshrate = 72;


//Alternative scrolling multiplier
//values: true/false (try to set this to true if scrolling is too slow/doesn't work)
var alternative_sesitivity_multiplier = false;


var minimal_jquery_version = 200;



var DEBUG = false;

//CODE STARTS HERE
if (window.top != window.self)  //don't run on frames or iframes
    return;



if (smoothness>100)
{
	smoothness = 100;
}

if (baserefreshrate <= 30 || baserefreshrate>144)
{
	baserefreshrate = 144;
}
refreshrate = baserefreshrate;

var animationduration = Math.round(1000/refreshrate);
//var relativeratio = Math.round(51-smoothness/2)/100;
var relativeratio = Math.round(1/(1+smoothness)*100)/100;
//var relativeratio = relativeratio;



var lastLoop = new Date;
//var lastrefreshrate = 0;
function gameLoop() { 
	var thisLoop = new Date;
    var refreshrate0 = 1000 / (thisLoop - lastLoop + 1);
    lastLoop = thisLoop;
	
	refreshrate = refreshrate + (refreshrate0-refreshrate)*0.01;
	refreshrate = Math.round(refreshrate);
	
	if (DEBUG)
	{
		console.log(refreshrate);
	}
	
	animationduration = Math.round(1000/(refreshrate));
	//var relativeratio = Math.round(51-smoothness/2)/100;
	relativeratio = Math.round(1/(1+smoothness*refreshrate/baserefreshrate)*100)/100;
}
gameLoop();


function InitSmoothscroll()
{

var startposition = false;
var targetposition = 0;
var position = 0;

var scrollfocus = $('body');
function hasScrollBarVisible(element)
{
  //return (document.documentElement.scrollHeight !== document.documentElement.clientHeight);
 
  // Get the computed style of the body element
  var cStyle = element.currentStyle||window.getComputedStyle(element, "");
 
  // Check the overflow and overflowY properties for "auto" and "visible" values
  var scrollbar1 = cStyle.overflow == "scroll" || cStyle.overflowY == "scroll";

	var scrollbar2 = cStyle.overflow == "auto" || cStyle.overflowY == "auto";
	
	var scrollbar = scrollbar1 || scrollbar2;
 
  return scrollbar;
}


function hasscrollbars(scrollfocus)
{
	var hasvisiblescrollbars = hasScrollBarVisible(scrollfocus);
	var parentelement = $(scrollfocus).parent();
	if ( $(parentelement))
	{
		//if ($(parentelement).is("textarea") || $(scrollfocus).is("textarea"))
		if ($(parentelement).is("textarea") || $(scrollfocus).is("textarea") || $(parentelement).is("article") || $(parentelement).is("article"))
		{
			return true;
		}
		else
		{
			if ($(parentelement).hasClass( "yt-scrollbar" ) || $(scrollfocus).hasClass( "yt-scrollbar" ))
			{
				return true;
			}
			return hasvisiblescrollbars;
		}
	}
	else
	{
		//scrollfocus = $('body');
		//maxposition = $(scrollfocus).height();
		return hasvisiblescrollbars;
	}
	return false;
}

function UpdatePosition(element)
{
	gameLoop();
	
	var positiondelta = $(element)[0].getAttribute( "positiondelta" );
	//var smoothdelta = Math.sqrt(positiondelta*positiondelta*relativeratio);
	
	var smoothdelta = Math.sqrt(positiondelta*positiondelta*relativeratio);
	
	if (positiondelta<0)
	{
		smoothdelta = smoothdelta*(-1);
	}
	
	//var relative = position - $(element).scrollTop();
	//console.log("smoothdelta:" + smoothdelta);
	
	if (Math.abs( (positiondelta-smoothdelta)) <= 1 )
	{
		$(element).stop();
		$(element).animate({
			scrollTop: '+=' + Math.round(positiondelta)
		}, animationduration, "linear", function() {
			$(element).attr( "positiondelta",0 );
			$(element)[0].setAttribute( "positiondelta",0 );
			if (DEBUG)
			{
				$(element).css( "border", "1px solid red" );
			}
		});
	}
	else
	{
		
		$(element).stop();
		$(element).animate({
			scrollTop: '+=' + Math.round(smoothdelta)
		}, animationduration, "linear", function() {
			$(element).attr( "positiondelta",positiondelta-smoothdelta );
			$(element)[0].setAttribute("positiondelta",positiondelta-smoothdelta );
			UpdatePosition(element);
			if (DEBUG)
			{
				$(element).css( "border", "1px solid red" );
			}
		});
	}
}


 function MouseScroll (x,y,e) {
	 scrollfocus = UpdateFocus(x,y);
	
	 var positiondelta = 0;
	 var lastscrolltop = 0;

	 var parentelement = $(scrollfocus).parent();
	 if ( $(parentelement))
	 {
		 if ($(parentelement).is("textarea") || $(scrollfocus).is("textarea"))
		 {
			 //return true;
		 }
		 else
		 {
			 if ( $(parentelement).height() < $(scrollfocus).height())
			 {
				 //maxposition = $(scrollfocus).height() - $(parentelement).height();
			 }
			 else
			 {
				 if ($(scrollfocus).height()==0)
				 {
					 //scrollfocus = $('body');
					 //maxposition = $(scrollfocus).height();
				 }

				 //scrollfocus = $('body');
				 //return MouseScroll (event);
				 //return true;
			 }
		 }
	 }
	 else
	 {
		 scrollfocus = $('body');
		 //maxposition = $(scrollfocus).height();
	 }
	 
	 var rolled = y;
	 
	 //console.log("rolled: " + rolled);
	
	 //if ($(scrollfocus).data("positiondelta" )==undefined)
	 //$embellishment.data("embellishmentid",1)
	 if ($(scrollfocus)[0].getAttribute("positiondelta")==undefined)
		 
		 {
			 positiondelta = 0;
			 //console.log("positiondelta: undefined");
		 }
	 else
		 {
			 positiondelta = $(scrollfocus)[0].getAttribute("positiondelta");
			 //console.log("positiondelta: " + positiondelta);
		 }
	 positiondelta = positiondelta*1;
	 
	 	var lastscrolltop = $(scrollfocus).scrollTop();
	 	$(scrollfocus).scrollTop(lastscrolltop+rolled);
	 	if ($(scrollfocus).scrollTop()==lastscrolltop)
		{
			if (!$(scrollfocus).is("body"))
			{
				focus = parentelement;
				//focus = UpdateFocus(event);
				//return MouseScroll (event);
				//console.log("false");
				return false;
			}
			else
			{
				//console.log("true");
				return false;
			}
		}
	 	else
		{
			e.preventDefault();
		}
	    $(scrollfocus).scrollTop(lastscrolltop);
	 
	 var direction = rolled/Math.abs(rolled);
	 //var positiondeltadelta = rolled*sensitivity + Math.sqrt(Math.abs(positiondelta/rolled))*acceleration*rolled;
	 
	 //var positiondeltadelta = rolled*(sensitivity+Math.sqrt(Math.abs(positiondelta))*acceleration);
	 //
	 var positiondeltadelta = Math.round(rolled*sensitivity + Math.sqrt(Math.abs(positiondelta-rolled*sensitivity))*acceleration*rolled*0.03/sensitivity);
	 
	 positiondelta = positiondelta + positiondeltadelta;
	
	 $(scrollfocus)[0].setAttribute("positiondelta",positiondelta );
	 
	 
	 UpdatePosition($(scrollfocus));
	 
	 //element.innerHTML = "";
	 //console.log("pos:" + position);
	 //event.preventDefault();
	 return true;
 }

 function canscroll(element, dir)
{
	var scrollable = $(element);
	var lastscrolltop = $(scrollable).scrollTop();
	$(scrollable).scrollTop(lastscrolltop+dir);
	if ($(scrollable).scrollTop()==lastscrolltop)
	{
		$(scrollable).scrollTop(lastscrolltop);
		return false;
	}
	else
	{
		$(scrollable).scrollTop(lastscrolltop);
		return true;
	}
	
}
function UpdateFocus(x,y) {
	 /*var dir = 0;
	 if ('wheelDelta' in event) {
		 dir = event.wheelDelta;
	 }
	 else {  // Firefox
		 // The measurement units of the detail and wheelDelta properties are different.
		 dir = event.detail*(-120);
	 }*/
	var dir = y;
	//console.log(dir);
	 //dir = dir*(-1);
	 //
	 	var nodelist = document.querySelectorAll( ":hover" );
		$(nodelist).stop();
	 	scrollfocus = $('body');
	 	for (var i = nodelist.length-1; i >= 0; i--) { 
				//var parent = nodelist[i-1];
			  var newfocus = nodelist[i];
				if (DEBUG)
				{
					$(newfocus).css( "border", "1px solid blue" );
					//var debugtimer = setTimeout(function(){ $(newfocus).css( "border", "0px solid white" ); }, 1000);
				}
				//if ($(newfocus).hasScrollBar3() && hasScrollBarVisible(newfocus) && canscroll(newfocus, dir) && hasscrollbars(newfocus))
				if (canscroll(newfocus, dir) && hasscrollbars(newfocus))
				{
					scrollfocus = $(newfocus);
					return newfocus;
				}
 		}

	 return scrollfocus;
 }
	
		$('body').bind({
			/*
			 mousewheel: function(e) {
			if (DEBUG)
			{
				console.log(scrollfocus);
			}
			  console.log("scrolling");
			  MouseScroll(e.originalEvent);
		  },
		  */
			
		  mousedown: function(e) {
			 if (DEBUG)
			{
				console.log(scrollfocus);
			}
			  if (scrollfocus)
			{
			  $(scrollfocus)[0].setAttribute( "positiondelta",0 );
			  $(scrollfocus).stop();
			}
				//scrollfocus = UpdateFocus(e.originalEvent);
			  //console.log("click");
		  }
		});
//Init(window);

(function(window,document) {

    var prefix = "", _addEventListener, onwheel, support;

    // detect event model
    if ( window.addEventListener ) {
        _addEventListener = "addEventListener";
    } else {
        _addEventListener = "attachEvent";
        prefix = "on";
    }

    // detect available wheel event
    support = "onwheel" in document.createElement("div") ? "wheel" : // Modern browsers support "wheel"
              document.onmousewheel !== undefined ? "mousewheel" : // Webkit and IE support at least "mousewheel"
              "DOMMouseScroll"; // let's assume that remaining browsers are older Firefox

    window.addWheelListener = function( elem, callback, useCapture ) {
        _addWheelListener( elem, support, callback, useCapture );

        // handle MozMousePixelScroll in older Firefox
        if( support == "DOMMouseScroll" ) {
            _addWheelListener( elem, "MozMousePixelScroll", callback, useCapture );
        }
    };

    function _addWheelListener( elem, eventName, callback, useCapture ) {
        elem[ _addEventListener ]( prefix + eventName, support == "wheel" ? callback : function( originalEvent ) {
            !originalEvent && ( originalEvent = window.event );

            // create a normalized event object
            var event = {
                // keep a ref to the original event object
                originalEvent: originalEvent,
                target: originalEvent.target || originalEvent.srcElement,
                type: "wheel",
                deltaMode: originalEvent.type == "MozMousePixelScroll" ? 0 : 1,
                deltaX: 0,
                deltaZ: 0,
                preventDefault: function() {
                    originalEvent.preventDefault ?
                        originalEvent.preventDefault() :
                        originalEvent.returnValue = false;
                }
            };
            
            // calculate deltaY (and deltaX) according to the event
            if ( support == "mousewheel" ) {
                event.deltaY = - 1/40 * originalEvent.wheelDelta;
                // Webkit also support wheelDeltaX
                originalEvent.wheelDeltaX && ( event.deltaX = - 1/40 * originalEvent.wheelDeltaX );
            } else {
                event.deltaY = originalEvent.detail;
            }

            // it's time to fire the callback
            return callback( event );

        }, useCapture || false );
    }

})(window,document);
	
addWheelListener( window, function( e ) { 
	var mul = 1;
	if (alternative_sesitivity_multiplier)
		mul = 40;
	//console.log( e.deltaY ); 
	MouseScroll(e.deltaX*mul,e.deltaY*mul, e);
});	

	
console.log("Smoothscroll loaded!");
}


var JQUERY = true;

//max retries
var r_max = 3;
var r_count = 0;

function Init() {
	//if (typeof jQuery == 'function')  {  
	if (typeof jQuery == 'undefined' || typeof $ == 'undefined' ) 
	{
		JQUERY = false;
	}
	if (JQUERY) {
		//console.log("Using existing jQuery..."); 
		//this.$ = this.jQuery = jQuery.noConflict(true);
		//var $ = jQuery.noConflict();
		//
		var versiontable = $.fn.jquery.split('.');
		var version = 0;
		for (var i = versiontable.length-1; i >= 0; i--) { 
				var power = versiontable.length-i;
				version += Math.pow(10,power-1)*versiontable[i];
		}
		
		if (version<=minimal_jquery_version)
		{
			JQUERY = false;
			console.log("jQuery OLD! Version: " + version);
		}
		else
		{
			console.log("jQuery OK! Version: " + version);
			InitSmoothscroll();	
		}
	}
	if (!JQUERY) 
	{
		console.log("Loading jQuery...");  
		//this.$ = this.jQuery = jQuery.noConflict(true);
		//var $ = jQuery.noConflict();

		//this.$ = this.jQuery = jQuery.noConflict(true);

		var filename = "https://code.jquery.com/jquery-2.2.3.js";
		var fileref=document.createElement('script')
			fileref.setAttribute("type","text/javascript")
			fileref.setAttribute("src", filename)

		if (typeof fileref!="undefined")
		{
			var scriptparent = document.getElementsByTagName("head")[0];
			
			if (typeof scriptparent=="undefined")
			{
				var scriptparent = document.createElement('head')
				document.getElementsByTagName("html")[0].appendChild(scriptparent);
			}
			scriptparent.appendChild(fileref);
		}
        
		
		JQUERY = true;
		
		if (r_count<r_max)
		{
			setTimeout(Init, 1000);
		}
		else
		{
			console.log("Failed to load smoothscroll!");
		}
		r_count++;
	}

}
console.log("Loading smoothscroll...");
Init();