Smoothscroll

Smooth scrolling on pages using javascript and jquery

Versión del día 23/11/2016. Echa un vistazo a la versión más reciente.

Tendrás que instalar una extensión para tu navegador como Tampermonkey, Greasemonkey o Violentmonkey si quieres utilizar este script.

Necesitarás instalar una extensión como Tampermonkey o Violentmonkey para instalar este script.

Necesitarás instalar una extensión como Tampermonkey o Violentmonkey para instalar este script.

Necesitarás instalar una extensión como Tampermonkey o Userscripts para instalar este script.

Necesitará instalar una extensión como Tampermonkey para instalar este script.

Necesitarás instalar una extensión para administrar scripts de usuario si quieres instalar este script.

(Ya tengo un administrador de scripts de usuario, déjame instalarlo)

Necesitará instalar una extensión como Stylus para instalar este estilo.

Necesitará instalar una extensión como Stylus para instalar este estilo.

Necesitará instalar una extensión como Stylus para instalar este estilo.

Necesitará instalar una extensión del gestor de estilos de usuario para instalar este estilo.

Necesitará instalar una extensión del gestor de estilos de usuario para instalar este estilo.

Necesitará instalar una extensión del gestor de estilos de usuario para instalar este estilo.

(Ya tengo un administrador de estilos de usuario, déjame instalarlo)

// ==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 2.4.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) (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;
  
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 = 3;
  
	var animationduration = Math.round(1000/(refreshrate*interpolation));
  
  	var relativeratio = Math.round( ( Math.pow(0.01,smoothness)*refreshrate/baserefreshrate )*1000000)/1000000;
	
	var positiondelta = scrollpositiondelta(element);
	  
	var smoothdelta = Math.sqrt(positiondelta*positiondelta*relativeratio)/interpolation;
  	var rounddelta = Math.ceil(smoothdelta);
  	
	//var smoothdelta = positiondelta*relativeratio;
  
	if (positiondelta<0)
	{
		smoothdelta = smoothdelta*(-1);
      	rounddelta = rounddelta*(-1);
	}

	if (Math.abs( rounddelta ) > 1 )
	{
		  scroll_timeout = setTimeout(function() {
			element.scrollTop = element.scrollTop + rounddelta;
			scrollpositiondelta(element, positiondelta - rounddelta);
			UpdatePosition(element);
		  }, animationduration);
	}
  	else
	{	
	
      if (Math.abs( positiondelta ) > 1 )
      {
      	
      	scroll_timeout = setTimeout(function() {
			element.scrollTop = element.scrollTop + rounddelta;
			scrollpositiondelta(element, positiondelta - rounddelta);
			UpdatePosition(element);
		  }, Math.abs(animationduration/smoothdelta) );
      
      }
      else
      {
          scroll_timeout = setTimeout(function() {
              element.scrollTop = element.scrollTop + rounddelta;
              scrollpositiondelta(element, 0);
            }, Math.abs(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;
   
   	//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(x,y);
              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);
	 
	 positiondelta = positiondelta*1 + positiondeltadelta*1;
	 
  	 scrollpositiondelta(scrollfocus, positiondelta)

	 clearTimeout(scroll_timeout);
	 
	 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 = //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 );

      delta = newdelta;
    }
	else
    {
		var olddelta = target.getAttribute("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" );
	 	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;
 }
	
  /*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 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!")
}

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.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()};
}

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

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

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