Smoothscroll

Smooth scrolling on pages using javascript and jquery

当前为 2017-08-17 提交的版本,查看 最新版本

  1. // ==UserScript==
  2. // @name Smoothscroll
  3. // @include http://*
  4. // @include https://*
  5. // @author Creec Winceptor
  6. // @description Smooth scrolling on pages using javascript and jquery
  7. // @namespace https://greasyfork.org/users/3167
  8. // @run-at document-load
  9. // @grant GM_getValue
  10. // @grant GM_setValue
  11. // @grant GM_registerMenuCommand
  12. // @version 2.9
  13. // ==/UserScript==
  14.  
  15. if (window.top != window.self) //don't run on frames or iframes
  16. return;
  17.  
  18. //DEFAULT SETTINGS HERE
  19. //DO NOT CHANGE ANYTHING HERE ANYMORE, USE SCRIPT COMMANDS -> CONFIGURE SMOOTHSCROLL
  20.  
  21.  
  22. //Smoothness factor value (how strong the smoothing effect is)
  23. //values: 1-(infinite) (default 1.00)
  24. var smoothness = 1;
  25.  
  26. //Scroll sensitivity
  27. //values: anything? (default 1.00)
  28. var sensitivity = 1;
  29.  
  30. //Acceleration sensitivity
  31. //values: anything? (default 1.50)
  32. var acceleration = 1;
  33.  
  34. //Refreshrate setting
  35. //values: 30-144 (default = 60/72/120/144 = same as your monitor hz)
  36. var refreshrate = 60;
  37.  
  38.  
  39. //Alternative scrolling multiplier
  40. //values: true/false (try to set this to true if scrolling is too slow/doesn't work)
  41. var alternative_sensitivity_multiplier = false;
  42.  
  43.  
  44. //CODE STARTS HERE
  45.  
  46. var DEBUG = false;
  47.  
  48. var WEBKIT = false;
  49.  
  50. //console.log("Loading smoothscroll...");
  51.  
  52. var baserefreshrate = 60; //DO NOT CHANGE THIS EVER
  53. if (smoothness>10)
  54. {
  55. smoothness = 10;
  56. }
  57.  
  58. if (refreshrate <= 30 || refreshrate>144)
  59. {
  60. refreshrate = 144;
  61. }
  62.  
  63. function InitSmoothscroll()
  64. {
  65. //LoadConfig();
  66.  
  67. //InitConfigmenu();
  68.  
  69. var startposition = false;
  70. var targetposition = 0;
  71. var position = 0;
  72.  
  73. //var scrollfocus = //ss$('body');
  74. var scrollfocus = document.body;
  75. var mousemoved = true;
  76. //var lastLoop = new Date;
  77. var nextLoop = 1;
  78. //var nextprint = lastLoop;
  79. var dynamicframedelay = 0;
  80. var dynamicrefreshrate = refreshrate;
  81. function fpscalcloop(animationduration) {
  82. var thisLoopDate = new Date;
  83. var thisLoop = thisLoopDate.getTime();
  84. //var fpscalc = 1000 / (thisLoop - lastLoop + 1);
  85. //lastLoop = thisLoop;
  86. //
  87. var delay = thisLoop - nextLoop;
  88. //var fpscalc = 1000 / (thisLoop - nextLoop + 1);
  89.  
  90. //refreshrate = refreshrate + (refreshrate0-refreshrate)*0.1;
  91. //refreshrate = Math.round(refreshrate);
  92. //animationduration = Math.round(1000/(refreshrate*3));
  93. //console.log(animationduration);
  94. //var relativeratio = Math.round(51-smoothness/2)/100;
  95. //relativeratio = Math.round(1/(1+smoothness*baserefreshrate/refreshrate)*100)/100;
  96. //
  97. //
  98. if (animationduration>=0)
  99. {
  100. nextLoop = thisLoop+animationduration;
  101. framedelay = Math.round(delay*refreshrate/1000);
  102.  
  103. if (framedelay>0)
  104. {
  105. dynamicframedelay = dynamicframedelay + framedelay*2;
  106. }
  107. else
  108. {
  109. if ( dynamicframedelay>0 && framedelay<1 )
  110. {
  111. dynamicframedelay = Math.floor(dynamicframedelay/2);
  112.  
  113. }
  114. }
  115.  
  116. refreshrate = refreshrate*1;
  117. dynamicrefreshrate = Math.round( (refreshrate*refreshrate/(refreshrate+dynamicframedelay))*100 )/100;
  118. }
  119. else
  120. {
  121. nextLoop = thisLoop;
  122. }
  123. //console.log("dynamicframedelay: " + dynamicframedelay);
  124. //console.log("dynamicrefreshrate: " + dynamicrefreshrate);
  125. }
  126.  
  127. function hasScrollBarVisible(element)
  128. {
  129. //return (document.documentElement.scrollHeight !== document.documentElement.clientHeight);
  130. // Get the computed style of the body element
  131. var cStyle = element.currentStyle||window.getComputedStyle(element, "");
  132. // Check the overflow and overflowY properties for "auto" and "visible" values
  133. var scrollbar1 = cStyle.overflow == "scroll" || cStyle.overflowY == "scroll";
  134.  
  135. var scrollbar2 = cStyle.overflow == "auto" || cStyle.overflowY == "auto";
  136. var scrollbar = scrollbar1 || scrollbar2;
  137. return scrollbar;
  138. }
  139.  
  140.  
  141. function hasscrollbars(scrollfocus)
  142. {
  143. var hasvisiblescrollbars = hasScrollBarVisible(scrollfocus);
  144. return hasvisiblescrollbars;
  145. /*
  146. var parentelement = //ss$(scrollfocus).parent();
  147. if ( //ss$(parentelement))
  148. {
  149. if (//ss$(parentelement).is("textarea") || //ss$(scrollfocus).is("textarea") || //ss$(parentelement).is("article") || //ss$(parentelement).is("article"))
  150. {
  151. return true;
  152. }
  153. else
  154. {
  155. if (//ss$(parentelement).hasClass( "yt-scrollbar" ) || //ss$(scrollfocus).hasClass( "yt-scrollbar" ))
  156. {
  157. return true;
  158. }
  159. return hasvisiblescrollbars;
  160. }
  161. }
  162. else
  163. {
  164. return hasvisiblescrollbars;
  165. }
  166. return false;
  167. */
  168. }
  169.  
  170. var scroll_timeout = null;
  171. function direction(number)
  172. {
  173. if (number>0)
  174. {
  175. return 1;
  176. }
  177. if (number<0)
  178. {
  179. return -1;
  180. }
  181. return 0;
  182. }
  183.  
  184. function UpdatePosition(element)
  185. {
  186. //fpscalcloop(0);
  187.  
  188. var basespeed = 2;
  189.  
  190. var animationduration = Math.floor(1000/(dynamicrefreshrate));
  191.  
  192. //var relativeratio = Math.ceil( interpolation*( Math.pow(0.003*interpolation,smoothness)/dynamicrefreshrate *baserefreshrate )*1000000)/1000000;
  193. //
  194. //var relativeratio = Math.ceil( ( 0.5/dynamicrefreshrate *baserefreshrate )*1000000)/1000000;
  195.  
  196. var relativeratio = 1-Math.pow( dynamicrefreshrate, -1/dynamicrefreshrate*smoothness*basespeed);
  197.  
  198. var positiondelta = scrollpositiondelta(element);
  199.  
  200. var dir = direction(positiondelta);
  201.  
  202. //console.log("positiondelta:" + positiondelta);
  203.  
  204. //var smoothdelta = Math.sqrt(positiondelta*positiondelta*relativeratio);
  205. //
  206. var smoothdelta = Math.abs(positiondelta*relativeratio);
  207.  
  208. var rounddelta = Math.ceil(smoothdelta);
  209.  
  210. //var smoothdelta = positiondelta*relativeratio;
  211.  
  212. if ( rounddelta > 1 )
  213. {
  214. element.scrollTop = element.scrollTop + rounddelta*dir;
  215. scrollpositiondelta(element, positiondelta - rounddelta*dir);
  216. scroll_timeout = setTimeout(function() {
  217. UpdatePosition(element);
  218. }, animationduration);
  219. fpscalcloop(animationduration);
  220. }
  221. else
  222. {
  223. if ( positiondelta*dir > 1 )
  224. {
  225. element.scrollTop = element.scrollTop + dir;
  226. scrollpositiondelta(element, positiondelta - dir);
  227. scroll_timeout = setTimeout(function() {
  228. UpdatePosition(element);
  229. }, (animationduration/smoothdelta));
  230. }
  231. else
  232. {
  233. element.scrollTop = element.scrollTop + rounddelta*dir;
  234. scrollpositiondelta(element, 0);
  235. //scroll_timeout = setTimeout(function() {
  236. //}, (animationduration/smoothdelta));
  237. }
  238. fpscalcloop( (animationduration/smoothdelta) );
  239. }
  240. }
  241.  
  242.  
  243. function MouseScroll (e) {
  244.  
  245. var mul = 1;
  246. if (!WEBKIT || alternative_sensitivity_multiplier)
  247. mul = 40;
  248. var x = e.deltaX*mul;
  249. var y = e.deltaY*mul;
  250. scrollfocus = UpdateFocus(e);
  251. /*if (mousemoved)
  252. {
  253. scrollfocus = UpdateFocus(x,y);
  254. mousemoved = false;
  255. }*/
  256. var positiondelta = 0;
  257. var lastscrolltop = 0;
  258.  
  259. var rolled = y;
  260. if (!canscroll(scrollfocus, rolled))
  261. {
  262. if (scrollfocus != document.documentElement)
  263. {
  264. //scrollfocus = UpdateFocus(e);
  265. return false;
  266. }
  267. else
  268. {
  269. //console.log("true");
  270. return false;
  271. }
  272. }
  273. else
  274. {
  275. if (e.defaultPrevented)
  276. {
  277. return true;
  278. }
  279. else
  280. {
  281. e.preventDefault();
  282. }
  283. }
  284. positiondelta = scrollpositiondelta(scrollfocus);
  285. var direction = rolled/Math.abs(rolled);
  286. //var positiondeltadelta = Math.round(rolled*sensitivity + Math.sqrt(Math.abs(positiondelta-rolled*sensitivity))*acceleration*rolled*0.2);
  287. //var positiondeltadelta = Math.round(Math.sqrt(Math.abs(positiondelta-rolled*sensitivity))*acceleration*rolled*0.2);
  288. var positiondeltadelta = rolled*sensitivity;
  289. var positiondeltacceleration = Math.sqrt(Math.abs(positiondelta/positiondeltadelta*acceleration));
  290. //console.log("positiondelta: "+positiondelta+" positiondeltadelta: " + positiondeltadelta + " positiondeltacceleration: " + positiondeltacceleration);
  291. positiondelta = Math.round(positiondelta*1 + positiondeltadelta*(1+positiondeltacceleration) );
  292. scrollpositiondelta(scrollfocus, positiondelta)
  293.  
  294. clearTimeout(scroll_timeout);
  295. fpscalcloop(-1);
  296. UpdatePosition(scrollfocus);
  297. //console.log(e);
  298. return true;
  299. }
  300.  
  301. function canscroll(element, dir)
  302. {
  303. if (dir>0)
  304. {
  305. dir = 1;
  306. }
  307. if (dir<0)
  308. {
  309. dir = -1;
  310. }
  311. var checkradius = 3; //pixels to try scrolling
  312. var canscroll0 = false;
  313.  
  314. var scrollable = element;
  315.  
  316. var lastscrolltop = scrollable.scrollTop;
  317.  
  318. scrollable.scrollTop = lastscrolltop+dir*checkradius;
  319.  
  320. if (scrollable.scrollTop!=lastscrolltop)
  321. {
  322. canscroll0 = true;
  323. }
  324.  
  325. scrollable.scrollTop = lastscrolltop;
  326.  
  327. return canscroll0;
  328. }
  329. function scrollpositiondelta(element, newdelta)
  330. {
  331. var target = element;
  332. var delta = 0;
  333. if (newdelta!=undefined)
  334. {
  335. target.positiondelta = newdelta;
  336. delta = newdelta;
  337. }
  338. else
  339. {
  340. var olddelta = target.positiondelta;
  341. if (olddelta!=undefined)
  342. {
  343. delta = olddelta;
  344. }
  345. }
  346. return delta*1;
  347. }
  348.  
  349. function UpdateFocus(e) {
  350. var x = e.deltaX;
  351. var y = e.deltaY;
  352. if (scrollfocus)
  353. {
  354. //scrollpositiondelta(scrollfocus, 0);
  355. }
  356. var dir = y;
  357. var nodelist = document.querySelectorAll( ":hover" );
  358. if (WEBKIT)
  359. {
  360. //scrollfocus = //ss$('body');
  361. scrollfocus = document.body;
  362. }
  363. else
  364. {
  365. //scrollfocus = //ss$('html');
  366. scrollfocus = document.documentElement;
  367. }
  368. for (var i = nodelist.length-1; i >= 0 ; i--) {
  369. var newfocus = nodelist[i];
  370. if (canscroll(newfocus, dir) && hasscrollbars(newfocus))
  371. {
  372. scrollfocus = newfocus;
  373. return newfocus;
  374. }
  375. }
  376.  
  377. return scrollfocus;
  378. }
  379. document.documentElement.addEventListener("wheel", function(e){
  380. MouseScroll(e);
  381. });
  382. document.documentElement.addEventListener("mousedown", function(e){
  383. if (scrollfocus)
  384. {
  385. scrollpositiondelta(scrollfocus, 0);
  386. }
  387. });
  388. /*
  389. document.documentElement.addEventListener("mousemove", function(e){
  390. mousemoved = true;
  391. });*/
  392.  
  393. //WEBKIT = 'webkitRequestAnimationFrame' in window;
  394. WEBKIT = document.compatMode == 'CSS1Compat' || document.compatMode == "BackCompat";
  395. //console.log("window: " + window);
  396. console.log("Smoothscroll loaded! Webkit: " + WEBKIT);
  397. $smoothscroll$ = true;
  398. }
  399.  
  400. function LoadConfig()
  401. {
  402. smoothness = GM_getValue( 'smoothness', smoothness );
  403. sensitivity = GM_getValue( 'sensitivity', sensitivity );
  404. acceleration = GM_getValue( 'acceleration', acceleration );
  405. refreshrate = GM_getValue( 'refreshrate', refreshrate );
  406. //alternative_sesitivity_multiplier = GM_getValue( 'alternative_sesitivity_multiplier', alternative_sesitivity_multiplier );
  407. //console.log("Config for smoothscroll loaded!")
  408. }
  409.  
  410. function SaveConfig()
  411. {
  412. GM_setValue( 'smoothness', document.getElementById('ss-smoothness').value)
  413. GM_setValue( 'sensitivity', document.getElementById('ss-sensitivity').value)
  414. GM_setValue( 'acceleration', document.getElementById('ss-acceleration').value)
  415. GM_setValue( 'refreshrate', document.getElementById('ss-refreshrate').value)
  416. //console.log(document.getElementById('ss-alternative_sesitivity_multiplier').checked)
  417. console.log("Config for smoothscroll saved!")
  418. location.reload();
  419. }
  420.  
  421. function CloseConfig()
  422. {
  423. var configbar = document.getElementById("ss-configbar");
  424. configbar.style.display = 'none';
  425. }
  426.  
  427. function InitConfigmenu()
  428. {
  429. //console.log("Initiating smoothscroll config...");
  430. var configbar = document.createElement('div');
  431. configbar.setAttribute("id","ss-configbar");
  432. //<tr><td>Sensitivity</td><td><input type="number" id="ss-sensitivity" min="0" max="100" value="' + sensitivity + '"></td><td> Scroll sensitivity (duh)</td></tr>
  433. 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>';
  434. var configparent = document.documentElement;
  435. configbar.style.width = '100%';
  436. configbar.style.display = 'none';
  437. configbar.style.position = 'absolute';
  438. configbar.style.zIndex = '9999';
  439. configbar.style.backgroundColor = 'white';
  440. configparent.insertBefore(configbar, configparent.childNodes[0]);
  441. document.getElementById("ss-close").onclick = function() {CloseConfig()};
  442. document.getElementById("ss-save").onclick = function() {SaveConfig()};
  443. }
  444.  
  445. var ConfigSmoothscroll = function()
  446. {
  447. console.log("opening");
  448. if (typeof $smoothscroll$ == 'undefined'){
  449. alert("Smoothscroll is not running properly on this page!");
  450. return;
  451. }
  452.  
  453. var configbar = document.getElementById("ss-configbar");
  454. configbar.style.display = 'block';
  455. window.scrollTo(0, 0);
  456. //ss$("html, body").animate({ scrollTop: 0 }, "slow");
  457. console.log("opening config...");
  458. }
  459.  
  460. if (typeof $smoothscroll$ == 'undefined'){
  461. //console.log("Initiating smoothscroll...");
  462. InitSmoothscroll();
  463. GM_registerMenuCommand("Smoothscroll config", ConfigSmoothscroll );
  464. }
  465. else
  466. {
  467. console.log("Smoothscroll already loaded!");
  468. }