Smoothscroll

Smooth scrolling on pages using javascript

Per 08-03-2018. Zie de nieuwste versie.

Voor het installeren van scripts heb je een extensie nodig, zoals Tampermonkey, Greasemonkey of Violentmonkey.

Voor het installeren van scripts heb je een extensie nodig, zoals {tampermonkey_link:Tampermonkey}.

Voor het installeren van scripts heb je een extensie nodig, zoals Tampermonkey of Violentmonkey.

Voor het installeren van scripts heb je een extensie nodig, zoals Tampermonkey of Userscripts.

Voor het installeren van scripts heb je een extensie nodig, zoals {tampermonkey_link:Tampermonkey}.

Voor het installeren van scripts heb je een gebruikersscriptbeheerder nodig.

(Ik heb al een user script manager, laat me het downloaden!)

Voor het installeren van gebruikersstijlen heb je een extensie nodig, zoals {stylus_link:Stylus}.

Voor het installeren van gebruikersstijlen heb je een extensie nodig, zoals {stylus_link:Stylus}.

Voor het installeren van gebruikersstijlen heb je een extensie nodig, zoals {stylus_link:Stylus}.

Voor het installeren van gebruikersstijlen heb je een gebruikersstijlbeheerder nodig.

Voor het installeren van gebruikersstijlen heb je een gebruikersstijlbeheerder nodig.

Voor het installeren van gebruikersstijlen heb je een gebruikersstijlbeheerder nodig.

(Ik heb al een beheerder - laat me doorgaan met de installatie!)

// ==UserScript==
// @name Smoothscroll
// @author       Creec Winceptor
// @description  Smooth scrolling on pages using javascript
// @namespace https://greatest.deepsurf.us/users/3167
// @include     *
// @version 5.1
// ==/UserScript==

//dev
var DEBUG = false;
var REFRESHRATE = 60;

//settings
var SMOOTHNESS = 0.5;
var ACCELERATION = 0.5;


function IsScrollable(element, dir)
{
	var checkradius = 2; //pixels to try scrolling

	if (dir>0)
	{
		dir = checkradius;
	}
	if (dir<0)
	{
		dir = -checkradius;
	}

	var originalscroll = element.scrollTop;
	var testscroll = Math.round(originalscroll + dir);
	element.scrollTop = testscroll;

	var scrollable = Math.round(element.scrollTop)==testscroll;
	element.scrollTop = originalscroll;

	return scrollable;
}

function HasScrollbars(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";

	//body or html always have scrollbars
	var scrollbar3 = element==document.body || element==document.documentElement;

	return scrollbar1 || scrollbar2 || scrollbar3;
}

function Direction(number)
{
  if (number>0)
  {
    return 1;
  }
  if (number<0)
  {
    return -1;
  }
  return 0;
}

function Timeout(element, newtimeout)
{
  if (newtimeout!=undefined)
  {
    var oldtimeout = element.ScrollTimeout;
    if (oldtimeout!=undefined)
    {
      clearTimeout(oldtimeout);
    }
    element.ScrollTimeout = newtimeout;
    return newtimeout;
  }
	else
  {
    var oldtimeout = element.ScrollTimeout;
    if (oldtimeout!=undefined)
    {
      return oldtimeout;
    }
    return null;
  }
}

function ScrollSubpixels(element, newvalue)
{
  if (newvalue!=undefined)
  {
    element.scrollsubpixels = newvalue;
    return newvalue;
  }
	else
  {
    var olddelta = element.scrollsubpixels;
    if (olddelta!=undefined)
    {
      return olddelta;
    }
    return 0;
  }
}
function ScrollPixels(element, newvalue)
{
  if (newvalue!=undefined)
  {
    element.scrollpixels = newvalue;
    
    ScrollSubpixels(element, 0);
    
    return newvalue;
  }
	else
  {
    var olddelta = element.scrollpixels;
    if (olddelta!=undefined)
    {
      return olddelta;
    }
    return 0;
  }
}

function GetTarget(e) {
  var direction = e.deltaY;
  var nodes = e.path;
  
  for (var i=0; i<nodes.length; i++) { 
    var node = nodes[i];
    if (IsScrollable(node, direction) && HasScrollbars(node))
    {
      if (DEBUG) {
        console.log("scrollbar: ");
        console.log(node);
      }
      return node;
    }
  }

  return null;
}

function AnimateScroll(target) {
  var updaterate = Math.floor(1000/(REFRESHRATE));
  
  var scrollsubpixels = ScrollSubpixels(target);
  var scrollpixels = ScrollPixels(target);

  var scrolldirection = Direction(scrollpixels);

  var scrollratio = 1-Math.pow( REFRESHRATE, -1/(REFRESHRATE*SMOOTHNESS));
  
  var scrollrate = scrollpixels*scrollratio;
  
  if (Math.abs(scrollpixels)>1) {
    
    var fullscrolls = Math.floor(Math.abs(scrollrate))*scrolldirection;
    var scrollsubpixelsadded = scrollrate - fullscrolls;

    var additionalscrolls = Math.floor(Math.abs(scrollsubpixels + scrollsubpixelsadded))*scrolldirection;
    var scrollsubpixelsleft = scrollsubpixels + scrollsubpixelsadded - additionalscrolls;

    ScrollPixels(target, scrollpixels-fullscrolls-additionalscrolls);
    ScrollSubpixels(target, scrollsubpixelsleft);
	
	  target.scrollTop += fullscrolls + additionalscrolls;

    Timeout(target, setTimeout(function() {

      AnimateScroll(target);
    }, updaterate));
  } else
  {
    ScrollPixels(target, 0);
  }
  
}

function StopScroll(e) {
  var nodes = e.path;
  
  for (var i=0; i<nodes.length; i++) { 
    var node = nodes[i];
    ScrollPixels(node, null);
    Timeout(node, null);
  }
}

function StartScroll(e, target) {
  var direction = e.deltaY;
  
  var scrollpixels = ScrollPixels(target);

  var accelerationratio = Math.sqrt(Math.abs(scrollpixels/direction*ACCELERATION));
  
  var acceleration = Math.round(direction*accelerationratio);

  ScrollPixels(target, scrollpixels + direction + acceleration);
 
  if (e.defaultPrevented)
  {
    return true;
  }
  else
  {
    AnimateScroll(target);
    e.preventDefault();
  }
}


function WheelEvent(e) {
  var target = GetTarget(e);
  
  if (target) {
    StartScroll(e, target);
  }
}

function ClickEvent(e) {
  StopScroll(e);
}

function Init()
{
  if (window.SMOOTHSCROLL) {
    console.log("Smoothscroll: already loaded");
    return null;
  }
  
  if (window.top != window.self) {
    console.log("Smoothscroll: ignoring iframe");
    return null;
  }
  
  document.documentElement.addEventListener("wheel", function(e){
    WheelEvent(e);
    
    if (DEBUG) {
      console.log(e);
    }
  });

  document.documentElement.addEventListener("mousedown", function(e){
    ClickEvent(e);
    
    if (DEBUG) {
      console.log(e);
    }
  });
  
  console.log("Smoothscroll: loaded");
  window.SMOOTHSCROLL = true;
}
Init();