Greasy Fork is available in English.

Smoothscroll

Smooth scrolling on pages using javascript and jquery

Από την 17/08/2017. Δείτε την τελευταία έκδοση.

// ==UserScript==
// @name Smoothscroll
// @include     http://*
// @include     https://*
// @author       Creec Winceptor
// @description  Smooth scrolling on pages using javascript and jquery
// @namespace https://greasyfork.org/users/3167
// @run-at document-load
// @grant       GM_getValue
// @grant       GM_setValue
// @grant       GM_registerMenuCommand
// @version 2.9
// ==/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) (default 1.00)
var smoothness = 1;

//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 DEBUG = false;

var WEBKIT = false;

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

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

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

function InitSmoothscroll()
{
  
  //LoadConfig();

  //InitConfigmenu();

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

//var scrollfocus = //ss$('body');
var scrollfocus = document.body;
var mousemoved = true;
  
//var lastLoop = new Date;  
var nextLoop = 1;
//var nextprint = lastLoop;
var dynamicframedelay = 0;
var dynamicrefreshrate = refreshrate;
function fpscalcloop(animationduration) { 
	var thisLoopDate = new Date;
  	var thisLoop = thisLoopDate.getTime();
    //var fpscalc = 1000 / (thisLoop - lastLoop + 1);
    //lastLoop = thisLoop;
    //
    var delay = thisLoop - nextLoop;
  	//var fpscalc = 1000 / (thisLoop - nextLoop + 1);
  

	
	//refreshrate = refreshrate + (refreshrate0-refreshrate)*0.1;
	//refreshrate = Math.round(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;
	//
	//
	if (animationduration>=0)
    {
       nextLoop = thisLoop+animationduration;
  	
      framedelay = Math.round(delay*refreshrate/1000);

      if (framedelay>0)
      {
           dynamicframedelay = dynamicframedelay + framedelay*2;
      }
      else
      {
        if ( dynamicframedelay>0 && framedelay<1 )
        {
           dynamicframedelay = Math.floor(dynamicframedelay/2);

        }
      }

      refreshrate = refreshrate*1;
  	 dynamicrefreshrate = Math.round( (refreshrate*refreshrate/(refreshrate+dynamicframedelay))*100 )/100;
    }
  	else
    {
        nextLoop = thisLoop;
    }
	
  
  //console.log("dynamicframedelay: " + dynamicframedelay);
  
  //console.log("dynamicrefreshrate: " + dynamicrefreshrate);
}

  
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 direction(number)
{
  if (number>0)
  {
    return 1;
  }
  if (number<0)
  {
    return -1;
  }
  return 0;
}

function UpdatePosition(element)
{
	//fpscalcloop(0);

    var basespeed = 2;

    var animationduration = Math.floor(1000/(dynamicrefreshrate));

    //var relativeratio = Math.ceil( interpolation*( Math.pow(0.003*interpolation,smoothness)/dynamicrefreshrate *baserefreshrate  )*1000000)/1000000;
    //
    //var relativeratio = Math.ceil( ( 0.5/dynamicrefreshrate *baserefreshrate  )*1000000)/1000000;

    var relativeratio = 1-Math.pow( dynamicrefreshrate, -1/dynamicrefreshrate*smoothness*basespeed);

    var positiondelta = scrollpositiondelta(element);

    var dir = direction(positiondelta);

    //console.log("positiondelta:" + positiondelta);

    //var smoothdelta = Math.sqrt(positiondelta*positiondelta*relativeratio);
    //
    var smoothdelta = Math.abs(positiondelta*relativeratio);

    var rounddelta = Math.ceil(smoothdelta);

    //var smoothdelta = positiondelta*relativeratio;

	if ( rounddelta > 1 )
	{	
      	element.scrollTop = element.scrollTop + rounddelta*dir;
			scrollpositiondelta(element, positiondelta - rounddelta*dir);
			
		  scroll_timeout = setTimeout(function() {
      	
			UpdatePosition(element);
		  }, animationduration);
      fpscalcloop(animationduration);
	}
  	else
	{	
		
      if ( positiondelta*dir > 1 )
      {
        
        element.scrollTop = element.scrollTop + dir;
			scrollpositiondelta(element, positiondelta - dir);
			
      	scroll_timeout = setTimeout(function() {
      		
			UpdatePosition(element);
		  }, (animationduration/smoothdelta));
      }
      else
      {
        element.scrollTop = element.scrollTop + rounddelta*dir;
              scrollpositiondelta(element, 0);
          //scroll_timeout = setTimeout(function() {
        	
              
            //}, (animationduration/smoothdelta));
      }
      fpscalcloop( (animationduration/smoothdelta) );
	}
}


 function MouseScroll (e) {

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

	 var rolled = y;
	 	
	 	if (!canscroll(scrollfocus, rolled))
		{
			if (scrollfocus != document.documentElement)	
			{
				//scrollfocus = UpdateFocus(e);
              return false;
			}
			else
			{
              	
              	//console.log("true");
				return false;
			}
		}
	 	else
		{
          	if (e.defaultPrevented)
            {
              return true;
            }
          	else
            {
              e.preventDefault();
            }
			
		}
   	 positiondelta = scrollpositiondelta(scrollfocus);
   
	 var direction = rolled/Math.abs(rolled);
	 
	 //var positiondeltadelta = Math.round(rolled*sensitivity + Math.sqrt(Math.abs(positiondelta-rolled*sensitivity))*acceleration*rolled*0.2);
	 //var positiondeltadelta = Math.round(Math.sqrt(Math.abs(positiondelta-rolled*sensitivity))*acceleration*rolled*0.2);
	 
	 var positiondeltadelta = rolled*sensitivity;
   var positiondeltacceleration = Math.sqrt(Math.abs(positiondelta/positiondeltadelta*acceleration));
   //console.log("positiondelta: "+positiondelta+" positiondeltadelta: " + positiondeltadelta + " positiondeltacceleration: " + positiondeltacceleration);
	 positiondelta = Math.round(positiondelta*1 + positiondeltadelta*(1+positiondeltacceleration) );
	 
  	 scrollpositiondelta(scrollfocus, positiondelta)

	 clearTimeout(scroll_timeout);
   
   		fpscalcloop(-1);
	 
	 UpdatePosition(scrollfocus);
   
   		//console.log(e);
	 
	 return true;
 }

 function canscroll(element, dir)
{
  	if (dir>0)
    {
      dir = 1;
    }
  	if (dir<0)
    {
      dir = -1;
    }
  	var checkradius = 3; //pixels to try scrolling
  
  	var canscroll0 = false;

	var scrollable = element;

	var lastscrolltop = scrollable.scrollTop;

	scrollable.scrollTop = lastscrolltop+dir*checkradius;

	if (scrollable.scrollTop!=lastscrolltop)
	{
		canscroll0 = true;
	}

	scrollable.scrollTop = lastscrolltop;

	return canscroll0;
}
  
  
function scrollpositiondelta(element, newdelta)
{
	var target = element;
  	var delta = 0;
  	if (newdelta!=undefined)
    {
    target.positiondelta = newdelta;
      delta = newdelta;
    }
	else
    {
		var olddelta = target.positiondelta;
      if (olddelta!=undefined)
      {
      	delta = olddelta;
      }
    }
  	return delta*1;
}

function UpdateFocus(e) {
    var x = e.deltaX;
   	var y = e.deltaY;
	if (scrollfocus)
      {
        //scrollpositiondelta(scrollfocus, 0);
      }
	var dir = y;
	 	var nodelist = document.querySelectorAll( ":hover" );
	 	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 newfocus = nodelist[i];
				if (canscroll(newfocus, dir) && hasscrollbars(newfocus))
				{
					scrollfocus = newfocus;
					return newfocus;
				}
 		}

	 return scrollfocus;
 }
  
document.documentElement.addEventListener("wheel", function(e){
    MouseScroll(e);
});
  
 document.documentElement.addEventListener("mousedown", function(e){
    if (scrollfocus)
      {
        scrollpositiondelta(scrollfocus, 0);
      }
});
  
  /*
document.documentElement.addEventListener("mousemove", function(e){
       mousemoved = true;
});*/

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

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!")
  
  location.reload();
}

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");
  
  //<tr><td>Sensitivity</td><td><input type="number" id="ss-sensitivity" min="0" max="100" value="' + sensitivity + '"></td><td> Scroll sensitivity (duh)</td></tr>
 	
  	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="10" value="' + smoothness + '"></td><td> Smoothness factor value (default 1.00)</td></tr><tr><td>Sensitivity</td><td><input type="number" id="ss-sensitivity" min="0" max="100" value="' + sensitivity + '"></td><td> Scroll sensitivity (default 1.00)</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 (default 1.00)</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.documentElement;
  	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()};
}

var ConfigSmoothscroll = function()
{
  console.log("opening");
  if (typeof $smoothscroll$ == 'undefined'){
    alert("Smoothscroll is not running properly on this page!");
    return;
  }

  var configbar = document.getElementById("ss-configbar");
  configbar.style.display = 'block';  
  window.scrollTo(0, 0);
  //ss$("html, body").animate({ scrollTop: 0 }, "slow");
  console.log("opening config...");
}

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