Greasy Fork is available in English.

Smoothscroll

Smooth scrolling on pages using javascript and jquery

Verze ze dne 01. 11. 2016. Zobrazit nejnovější verzi.

// ==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    none
// @grant       GM_getValue
// @grant       GM_setValue
// @grant       GM_registerMenuCommand
// @version 2.0
// ==/UserScript==

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





//DEFAULT SETTINGS HERE 
//DO NOT CHANGE ANYTHING HERE ANYMORE, USE SCRIPT COMMANDS -> CONFIGURE SMOOTHSCROLL






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

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

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

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


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


//CODE STARTS HERE

var minimal_jquery_version = 200;

//max retries
//var jquery_retry_max = 5;
//var jquery_retry_count = 0;


var DEBUG = false;

var WEBKIT = false;

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

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


var baserefreshrate = 60; //DO NOT CHANGE THIS EVER
if (smoothness>100)
{
	smoothness = 100;
}

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

//var animationduration = Math.round(1000/refreshrate*3);
//var relativeratio = Math.round(51-smoothness/2)/100;
//var relativeratio = Math.round(1/(1+smoothness*baserefreshrate/refreshrate)*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.1;
	refreshrate = Math.round(refreshrate);
	
	if (DEBUG)
	{
		console.log(refreshrate);
	}
	
	//animationduration = Math.round(1000/(refreshrate*3));
	//console.log(animationduration);
	//var relativeratio = Math.round(51-smoothness/2)/100;
	relativeratio = Math.round(1/(1+smoothness*baserefreshrate/refreshrate)*100)/100;
}
gameLoop();*/


function InitSmoothscroll()
{
  
  LoadConfig();

  InitConfigmenu();

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

//var scrollfocus = //ss$('body');
var scrollfocus = document.body;
var mousemoved = true;
  
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);
	return hasvisiblescrollbars;
	
	/*
	var parentelement = //ss$(scrollfocus).parent();
	if ( //ss$(parentelement))
	{
		if (//ss$(parentelement).is("textarea") || //ss$(scrollfocus).is("textarea") || //ss$(parentelement).is("article") || //ss$(parentelement).is("article"))
		{
			return true;
		}
		else
		{
			if (//ss$(parentelement).hasClass( "yt-scrollbar" ) || //ss$(scrollfocus).hasClass( "yt-scrollbar" ))
			{
				return true;
			}
			return hasvisiblescrollbars;
		}
	}
	else
	{
		return hasvisiblescrollbars;
	}
	return false;
	*/
}

var scroll_timeout = null;

function UpdatePosition(element)
{
	//gameLoop();
	var interpolation = 2;
  
	var animationduration = Math.round(1000/(refreshrate*interpolation));
  
  	var relativeratio = Math.round(( 1/(1+smoothness)/interpolation*refreshrate/baserefreshrate )*1000)/1000;
	
	var positiondelta = scrollpositiondelta(element);
	
	//console.log(positiondelta);
                  
	var smoothdelta = Math.sqrt(positiondelta*positiondelta*relativeratio);
	
	if (positiondelta<0)
	{
		smoothdelta = smoothdelta*(-1);
	}
	
	//console.log(smoothdelta);
	
	//console.log(positiondelta-smoothdelta);
	
	
	
	if (Math.abs( (positiondelta-smoothdelta) ) <= 1 )
	//if (Math.abs( smoothdelta ) < 1 )
	{	
		/*
		//ss$(element).stop();
		ss$(element).animate({
			scrollTop: '+=' + Math.round(positiondelta)
		}, animationduration, "linear", function() {
          	scrollpositiondelta(element, 0);
			if (DEBUG)
			{
				//ss$(element).css( "border", "1px solid red" );
			}
		});
		*/
		
		scroll_timeout = setTimeout(function() {
			element.scrollTop = element.scrollTop + smoothdelta;
			scrollpositiondelta(element, 0);
		  }, animationduration);
		

	}
	else
	{
		/*
		//ss$(element).stop();
		ss$(element).animate({
			scrollTop: '+=' + Math.round(smoothdelta)
		}, animationduration, "linear", function() {
			scrollpositiondelta(element, positiondelta-smoothdelta);
			UpdatePosition(element);
			if (DEBUG)
			{
				//ss$(element).css( "border", "1px solid red" );
			}
		});
		*/
		
		  scroll_timeout = setTimeout(function() {
			element.scrollTop = element.scrollTop + smoothdelta;
			scrollpositiondelta(element, positiondelta - smoothdelta);
			UpdatePosition(element);
		  }, animationduration);
	}
}


 function MouseScroll (e) {
   //gameLoop();
   
   	var mul = 1;
	if (!WEBKIT || alternative_sensitivity_multiplier)
		mul = 40;
   	var x = e.deltaX*mul;
   	var y = e.deltaY*mul;
   
   	if (mousemoved)
    {
      scrollfocus = UpdateFocus(x,y);
      mousemoved = false;
    }
   
	 var positiondelta = 0;
	 var lastscrolltop = 0;

	 //var parentelement = //ss$(scrollfocus).parent();
	 
	 var rolled = y;
	 
	 	//var lastscrolltop = //ss$(scrollfocus).scrollTop();
	 	
	 	if (!canscroll(scrollfocus, rolled))
		{
			//if (!//ss$(scrollfocus).is("html"))
			if (scrollfocus != document.documentElement)	
			{
				scrollfocus = UpdateFocus(x,y);
              return false;
			}
			else
			{
              	
              	//console.log("true");
				return false;
			}
		}
	 	else
		{
			e.preventDefault();
		}
   	 positiondelta = scrollpositiondelta(scrollfocus);
   
   
	//console.log("positiondelta = " + positiondelta);
	
	 var direction = rolled/Math.abs(rolled);
	 
	 var positiondeltadelta = Math.round(rolled*sensitivity + Math.sqrt(Math.abs(positiondelta-rolled*sensitivity))*acceleration*rolled*0.09/sensitivity);
	 
	 positiondelta = positiondelta*1 + positiondeltadelta*1;
	 
	 //console.log("positiondelta + " + positiondeltadelta + " = " + positiondelta);

  	 scrollpositiondelta(scrollfocus, positiondelta)
	 
	 //UpdatePosition(//ss$(scrollfocus));
	 clearTimeout(scroll_timeout);
	 
	 UpdatePosition(scrollfocus);
	 
	 return true;
 }

 function canscroll(element, dir)
{
  	//console.log(dir);
  	if (dir>0)
    {
      dir = 1;
    }
  	if (dir<0)
    {
      dir = -1;
    }
  	var checkradius = 3; //pixels to try scrolling
  
  	var canscroll0 = false;
	//var scrollable = //ss$(element);
	var scrollable = element;
	//var lastscrolltop = //ss$(scrollable).scrollTop();
	var lastscrolltop = scrollable.scrollTop;

	//ss$(scrollable).scrollTop(lastscrolltop+dir*checkradius);
	scrollable.scrollTop = lastscrolltop+dir*checkradius;
	
	//if (//ss$(scrollable).scrollTop()!=lastscrolltop)
	if (scrollable.scrollTop!=lastscrolltop)
	{
		canscroll0 = true;
	}
  	//ss$(scrollable).scrollTop(lastscrolltop);
	scrollable.scrollTop = lastscrolltop;
  	//console.log("canscroll0: " + //ss$(element).prev().prop('nodeName') + " : " + canscroll0);
	return canscroll0;
}
  
  
function scrollpositiondelta(element, newdelta)
{
  	//var target = //ss$(element);
	var target = element;
  	var delta = 0;
  	if (newdelta!=undefined)
    {
      //console.log(dir);
      var dir = 0;
      if (newdelta>0)
      {
        dir = 1;
      }
      if (newdelta<0)
      {
        dir = -1;
      }
	  target.setAttribute("positiondelta", newdelta );
	  //console.log(newdelta);
	  ////ss$(target)[0].setAttribute("positiondelta", newdelta );
	  //ss$( target ).data( "positiondelta", newdelta );
      delta = newdelta;
    }
	else
    {
		var olddelta = target.getAttribute("positiondelta");
      //var olddelta = //ss$(target)[0].getAttribute("positiondelta");
      //var olddelta = //ss$( target ).data( "positiondelta" );
      if (olddelta!=undefined)
      {
      	delta = olddelta;
      }
    }
  	return delta*1;
}

function UpdateFocus(x,y) {
	if (scrollfocus)
      {
        //scrollpositiondelta(scrollfocus, 0);
        //ss$(scrollfocus).stop();
      }
	var dir = y;
	 	var nodelist = document.querySelectorAll( ":hover" );
		////ss$(nodelist).stop();
  	//console.log(nodelist);
	 	if (WEBKIT)
        {
        	//scrollfocus = //ss$('body');
			scrollfocus = document.body;
        }
  		else
        {
            //scrollfocus = //ss$('html');
			scrollfocus = document.documentElement;
        }
  		
	 	for (var i = nodelist.length-1; i >= 0 ; i--) { 
				//var parent = nodelist[i-1];
			  var newfocus = nodelist[i];
				if (DEBUG)
				{
					//ss$(newfocus).css( "border", "1px solid blue" );
					//var debugtimer = setTimeout(function(){ //ss$(newfocus).css( "border", "0px solid white" ); }, 1000);
				}
				//if (//ss$(newfocus).hasScrollBar3() && hasScrollBarVisible(newfocus) && canscroll(newfocus, dir) && hasscrollbars(newfocus))
				if (canscroll(newfocus, dir) && hasscrollbars(newfocus))
				{
					//scrollfocus = ss$(newfocus);
					scrollfocus = newfocus;
					return newfocus;
				}
 		}

	 return scrollfocus;
 }
	
  /*ss$('html').bind({
    
	mousemove: function(e) {
      mousemoved = true;
    },
    
    mousedown: function(e) {
      if (DEBUG)
      {
        console.log(scrollfocus);
      }
      if (scrollfocus)
      {
        scrollpositiondelta(scrollfocus, 0);
        ////ss$(scrollfocus).stop();
      }
      scrollfocus = UpdateFocus(0,0);
      //console.log("click");
    }
  });
	*/
	
  
document.documentElement.addEventListener("wheel", function(e){
    MouseScroll(e);
	mousemoved = true;
  //console.log("scrolling");
});
  

  //WEBKIT = 'webkitRequestAnimationFrame' in window;
  WEBKIT = document.compatMode == 'CSS1Compat' || document.compatMode == "BackCompat";
  
  //console.log("window: " + window);
  console.log("Smoothscroll initiated! Webkit: " + WEBKIT);
  
  $smoothscroll$ = true;
}

/*
var JQUERY = false;
var OWNJQUERY = false;
var OLDJQUERY = false;

var old$;
var oldjQuery;

var ss$;
*/

/*
function jQueryVersion(temp$)
{
  if (typeof temp$ == 'undefined' || typeof temp$.fn == 'undefined' || typeof temp$.fn.jquery == 'undefined')
  {
    return 0;
  }

  var versiontable = temp$.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];
  }
  return version;
}
*/

function LoadConfig()
{
  smoothness = GM_getValue( 'smoothness', smoothness );
  sensitivity = GM_getValue( 'sensitivity', sensitivity );
  acceleration = GM_getValue( 'acceleration', acceleration );
  refreshrate = GM_getValue( 'refreshrate', refreshrate );
  //alternative_sesitivity_multiplier = GM_getValue( 'alternative_sesitivity_multiplier', alternative_sesitivity_multiplier );
  console.log("Config for smoothscroll loaded!")
}

function SaveConfig()
{
  GM_setValue( 'smoothness', document.getElementById('ss-smoothness').value)
  GM_setValue( 'sensitivity', document.getElementById('ss-sensitivity').value)
  GM_setValue( 'acceleration', document.getElementById('ss-acceleration').value)
  GM_setValue( 'refreshrate', document.getElementById('ss-refreshrate').value)
  //console.log(document.getElementById('ss-alternative_sesitivity_multiplier').checked)
  console.log("Config for smoothscroll saved!")
}

function CloseConfig()
{
 	var configbar = document.getElementById("ss-configbar");
  configbar.style.display = 'none';  
}

function InitConfigmenu()
{
  	console.log("Initiating smoothscroll config...");
    var configbar = document.createElement('div');
    configbar.setAttribute("id","ss-configbar");
 	
  	configbar.innerHTML = '<div style="display:block; width: 100%; height: auto; border: 0px solid #aaaaaa; background-color: grey;">Config page for smoothscroll (refresh page for changes to apply!) Made by: Creec Winceptor</div><div id="ss-config" style="margin:3px; display:block; width: auto; height: auto; border: 0px solid #554433;"><table style="width:auto;"><tr><td>Smoothness</td><td><input type="number" id="ss-smoothness" min="0" max="100" value="' + smoothness + '"></td><td> Smoothness factor value (how strong the smoothing effect is)</td></tr><tr><td>Sensitivity</td><td><input type="number" id="ss-sensitivity" min="0" max="100" value="' + sensitivity + '"></td><td> Scroll sensitivity (duh)</td></tr><tr><td>Acceleration</td><td><input type="number" id="ss-acceleration" min="0" max="100" value="' + acceleration + '"></td><td> Acceleration of continuous scroll action (saves your finger)</td></tr><tr><td>Refreshrate</td><td><input type="number" id="ss-refreshrate" min="1" max="100" value="' + refreshrate + '"></td><td>Refreshrate of scrollanimation (60Hz default)</td></tr></table></div><div style="width: 100%; height: auto; text-align: center; background-color: grey;"><input id="ss-save" type="button" value="Save config" style="width: 44%; height: auto; border: 0px solid #aaaaaa; margin: 3px"/><input id="ss-close" type="button" value="Close config" style="width: 44%; height: auto; border: 0px solid #aaaaaa; margin: 3px"/><div>';
  	
  	
    var configparent = document.getElementsByTagName("body")[0];
  	configbar.style.width = '100%';  
  	configbar.style.display = 'none';  
  	configbar.style.position = 'absolute';
	configbar.style.zIndex = '9999';
  	configbar.style.backgroundColor = 'white';
  
  	configparent.insertBefore(configbar, configparent.childNodes[0]);
  	
  	document.getElementById("ss-close").onclick = function() {CloseConfig()};
  
  	document.getElementById("ss-save").onclick = function() {SaveConfig()};
  
    //ss$("#ss-close").click(function() {
      ////ss$("#ss-config").animate({
      //  height: '0'
      //}, 100);
      //ss$("#ss-configbar").hide("slow");
    //});
     //ss$("#ss-save").click(function() {
      //SaveConfig();
    //});
  
  ////ss$("#ss-configbar").hide();
}

function ConfigSmoothscroll()
{
  if (typeof $smoothscroll$ == 'undefined'){
    alert("Smoothscroll is not running properly on this page!");
    return;
  }
  ////ss$("#ss-config").animate({
    //height: '100px'
  //}, 100);
  //ss$("#ss-configbar").show("slow");
  var configbar = document.getElementById("ss-configbar");
  configbar.style.display = 'block';  
  //ss$("html, body").animate({ scrollTop: 0 }, "slow");
  console.log("opening config...");
}

/*
function LoadjQuery(loading)
{
  if (loading)
  {
    console.log("Waiting for jQuery...");
	if (typeof $ == 'undefined' || typeof jQuery == 'undefined' || jQueryVersion($)<minimal_jquery_version)
    {
      if (jquery_retry_count<jquery_retry_max)
      {
        jquery_retry_count++;
        setTimeout(function() {
            LoadjQuery(true);
        }, 100);
      }
      else
      {
        console.log("Failed to load smoothscroll!");
        if (typeof old$ != 'undefined') {
          $ = old$;
        }
        if (typeof oldjQuery != 'undefined') {
          jQuery = oldjQuery;
        } 
      }
    }
    else
    {
      //ss$ = $;
      //ssjQuery = jQuery;
      console.log("jQuery loaded!");
      
      if (typeof old$ != 'undefined') {
        $ = old$;
      }
      if (typeof oldjQuery != 'undefined') {
        jQuery = oldjQuery;
      } 
      console.log("Page jQuery version: " + jQueryVersion(old$));
      console.log("Script jQuery version: " + jQueryVersion(//ss$));
      
      InitSmoothscroll();
    }
  }
  else
  {
    console.log("Loading own jQuery...");
	jquery_retry_count = 0;
    
    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);
    }
    
    LoadjQuery(true);
  }
}
*/

/*
function Init()
{

  if (typeof $smoothscroll$ == 'undefined' ) 
  {
    var sitejquery = !(typeof $ == 'undefined' || typeof jQuery == 'undefined');
    if (sitejquery && jQueryVersion($)>=minimal_jquery_version) 
    {
      //ss$ = $;
      //ssjQuery = jQuery;
      console.log("Reusing page jQuery...");
      console.log("Page jQuery version: " + jQueryVersion($));
      InitSmoothscroll();
    }
	else
    {
        if (typeof $ != 'undefined' && typeof old$ == 'undefined') 
        {
          old$ = $;
        }
        if (typeof jQuery != 'undefined' && typeof oldjQuery == 'undefined') 
        {
          oldjQuery = jQuery;
        }
      
      	LoadjQuery(false);   	
    }
  }
}
*/

if (typeof $smoothscroll$ == 'undefined'){
	console.log("Initiating smoothscroll...");
	//Init();
	InitSmoothscroll();	
	GM_registerMenuCommand("Configurate smoothscroll", ConfigSmoothscroll);
}
else
{
  console.log("Smoothscroll already loaded!");
}