Smoothscroll

Smooth scrolling on pages using javascript and jquery

目前為 2016-11-23 提交的版本,檢視 最新版本

// ==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!");
}