Smoothscroll

Smooth scrolling on pages using javascript and jquery

Verzia zo dňa 17.07.2016. Pozri najnovšiu verziu.

Na nainštalovanie skriptu si budete musieť nainštalovať rozšírenie, ako napríklad Tampermonkey, Greasemonkey alebo Violentmonkey.

Na nainštalovanie skriptu si budete musieť nainštalovať rozšírenie, ako napríklad Tampermonkey, % alebo Violentmonkey.

Na nainštalovanie skriptu si budete musieť nainštalovať rozšírenie, ako napríklad Tampermonkey, % alebo Violentmonkey.

Na nainštalovanie skriptu si budete musieť nainštalovať rozšírenie, ako napríklad Tampermonkey alebo Userscripts.

Na inštaláciu tohto skriptu je potrebné nainštalovať rozšírenie, ako napríklad Tampermonkey.

Na inštaláciu tohto skriptu je potrebné nainštalovať rozšírenie správcu používateľských skriptov.

(Už mám správcu používateľských skriptov, nechajte ma ho nainštalovať!)

Na inštaláciu tohto štýlu je potrebné nainštalovať rozšírenie, ako napríklad Stylus.

Na inštaláciu tohto štýlu je potrebné nainštalovať rozšírenie, ako napríklad Stylus.

Na inštaláciu tohto štýlu je potrebné nainštalovať rozšírenie, ako napríklad Stylus.

Na inštaláciu tohto štýlu je potrebné nainštalovať rozšírenie správcu používateľských štýlov.

Na inštaláciu tohto štýlu je potrebné nainštalovať rozšírenie správcu používateľských štýlov.

Na inštaláciu tohto štýlu je potrebné nainštalovať rozšírenie správcu používateľských štýlov.

(Už mám správcu používateľských štýlov, nechajte ma ho nainštalovať!)

// ==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-load
// @grant    none
// @grant       GM_getValue
// @grant       GM_setValue
// @grant       GM_registerMenuCommand
// @version 1.0.9.1
// ==/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 baserefreshrate = 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...");

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*refreshrate/baserefreshrate)*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);
	}
  //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()
{
  
  LoadConfig();

  InitConfigmenu();

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

var scrollfocus = ss$('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);
	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;
}

function UpdatePosition(element)
{
	gameLoop();
	
	var positiondelta = ss$(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 - ss$(element).scrollTop();
	//console.log("smoothdelta:" + smoothdelta);
	
	if (Math.abs( (positiondelta-smoothdelta)) <= 1 )
	{
		ss$(element).stop();
		ss$(element).animate({
			scrollTop: '+=' + Math.round(positiondelta)
		}, animationduration, "linear", function() {
			ss$(element).attr( "positiondelta",0 );
			ss$(element)[0].setAttribute( "positiondelta",0 );
			if (DEBUG)
			{
				ss$(element).css( "border", "1px solid red" );
			}
		});
	}
	else
	{
		
		ss$(element).stop();
		ss$(element).animate({
			scrollTop: '+=' + Math.round(smoothdelta)
		}, animationduration, "linear", function() {
			ss$(element).attr( "positiondelta",positiondelta-smoothdelta );
			ss$(element)[0].setAttribute("positiondelta",positiondelta-smoothdelta );
			UpdatePosition(element);
			if (DEBUG)
			{
				ss$(element).css( "border", "1px solid red" );
			}
		});
	}
}


 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();
	 	ss$(scrollfocus).scrollTop(lastscrolltop+rolled);
	 	if (ss$(scrollfocus).scrollTop()==lastscrolltop)
		{
			if (!ss$(scrollfocus).is("html"))
			{
				focus = parentelement;
				//focus = UpdateFocus(event);
				//return MouseScroll (event);
				//console.log("false");
				//return false;
			}
			else
			{
              	//console.log("true");
				//return false;
			}
		}
	 	else
		{
			e.preventDefault();
		}
	    ss$(scrollfocus).scrollTop(lastscrolltop);
   
	 	 if (ss$(scrollfocus)[0].getAttribute("positiondelta")==undefined)
		 
		 {
			 positiondelta = 0;
			 //console.log("positiondelta: undefined");
		 }
	 else
		 {
			 positiondelta = ss$(scrollfocus)[0].getAttribute("positiondelta");
			 //console.log("positiondelta: " + positiondelta);
		 }
	 positiondelta = positiondelta*1;
   
	 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.05/sensitivity);
	 
	 positiondelta = positiondelta + positiondeltadelta;
	
	 ss$(scrollfocus)[0].setAttribute("positiondelta",positiondelta );
	 
	 
	 UpdatePosition(ss$(scrollfocus));
	 
	 return true;
 }

 function canscroll(element, dir)
{
	var scrollable = ss$(element);
	var lastscrolltop = ss$(scrollable).scrollTop();
	ss$(scrollable).scrollTop(lastscrolltop+dir);
	if (ss$(scrollable).scrollTop()==lastscrolltop)
	{
		ss$(scrollable).scrollTop(lastscrolltop);
		return false;
	}
	else
	{
		ss$(scrollable).scrollTop(lastscrolltop);
		return true;
	}
	
}

function UpdateFocus(x,y) {
	
	var dir = y;
	 	var nodelist = document.querySelectorAll( ":hover" );
		//ss$(nodelist).stop();
  	//console.log(nodelist);
	 	if (WEBKIT)
        {
        	scrollfocus = ss$('body');
        }
  		else
        {
            scrollfocus = ss$('html');
        }
  		
	 	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);
					return newfocus;
				}
 		}

	 return scrollfocus;
 }
	
  ss$('html').bind({
    
			 //mousewheel: function(e) {
			//if (DEBUG)
			//{
			//	console.log(scrollfocus);
			//}
			  //console.log("scrolling");
			  //MouseScroll(e.originalEvent);
		  //},
	mousemove: function(e) {
      mousemoved = true;
    },

    mousedown: function(e) {
      if (DEBUG)
      {
        console.log(scrollfocus);
      }
      if (scrollfocus)
      {
        ss$(scrollfocus)[0].setAttribute( "positiondelta",0 );
        ss$(scrollfocus).stop();
      }
      scrollfocus = UpdateFocus(0,0);
      //console.log("click");
    }
  });
//Init(window);

	
  
document.documentElement.addEventListener("wheel", function(e){
    MouseScroll(e);
  //console.log("scrolling");
});
  
  /*var oldpos = ss$('body').scrollTop();
  ss$('body').scrollTop(1);
  var iswebkit = ss$('body').scrollTop()>0;
  ss$('body').scrollTop(oldpos);*/

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

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 );
  baserefreshrate = GM_getValue( 'baserefreshrate', baserefreshrate );
  //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( 'baserefreshrate', document.getElementById('ss-baserefreshrate').value)
  //console.log(document.getElementById('ss-alternative_sesitivity_multiplier').checked)
  console.log("Config for smoothscroll saved!")
}

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-baserefreshrate" min="1" max="100" value="' + baserefreshrate + '"></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]);
  	
    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 ss$ == 'undefined'){
    alert("Smoothscroll is not running properly on this page!");
    return;
  }
  //ss$("#ss-config").animate({
  //  height: '100px'
  //}, 100);
  ss$("#ss-configbar").show("slow");
  ss$("html, body").animate({ scrollTop: 0 }, "slow");
}

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 ss$ == '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 ss$ == 'undefined'){
	console.log("Initiating smoothscroll...");
	Init();
	//InitSmoothscroll();	
	GM_registerMenuCommand("Configurate smoothscroll", ConfigSmoothscroll);
}
else
{
  console.log("Smoothscroll already loaded!");
}