Smoothscroll

Smooth scrolling on pages using javascript and jquery

当前为 2016-06-18 提交的版本,查看 最新版本

  1. // ==UserScript==
  2. // @name Smoothscroll
  3. // @include http*
  4. // @author Creec Winceptor
  5. // @description Smooth scrolling on pages using javascript and jquery
  6. // @namespace https://greasyfork.org/users/3167
  7. // @run-at document-idle
  8. // @grant none
  9. // @grant GM_getValue
  10. // @grant GM_setValue
  11. // @grant GM_registerMenuCommand
  12. // @version 1.0.5
  13. // ==/UserScript==
  14.  
  15.  
  16.  
  17.  
  18.  
  19.  
  20.  
  21. //DEFAULT SETTINGS HERE
  22. //DO NOT CHANGE ANYTHING HERE ANYMORE, USE SCRIPT COMMANDS -> CONFIGURE SMOOTHSCROLL
  23.  
  24.  
  25.  
  26.  
  27.  
  28.  
  29. //Smoothness factor value (how strong the smoothing effect is)
  30. //values: 1-(infinite) (default = 30)
  31. var smoothness = 20;
  32.  
  33. //Scroll sensitivity
  34. //values: anything? (default 1.00)
  35. var sensitivity = 1;
  36.  
  37. //Acceleration sensitivity
  38. //values: anything? (default 1.50)
  39. var acceleration = 1;
  40.  
  41. //Refreshrate setting
  42. //values: 30-144 (default = 60/72/120/144 = same as your monitor hz)
  43. var baserefreshrate = 60;
  44.  
  45.  
  46. //Alternative scrolling multiplier
  47. //values: true/false (try to set this to true if scrolling is too slow/doesn't work)
  48. var alternative_sesitivity_multiplier = false;
  49.  
  50.  
  51. //CODE STARTS HERE
  52.  
  53. var minimal_jquery_version = 200;
  54.  
  55. //max retries
  56. var jquery_retry_max = 10;
  57. var jquery_retry_count = 0;
  58.  
  59.  
  60. var DEBUG = false;
  61.  
  62. var WEBKIT = false;
  63.  
  64. //this.$ = this.jQuery = jQuery.noConflict(true);
  65.  
  66. if (window.top != window.self) //don't run on frames or iframes
  67. return;
  68.  
  69.  
  70.  
  71. if (smoothness>100)
  72. {
  73. smoothness = 100;
  74. }
  75.  
  76. if (baserefreshrate <= 30 || baserefreshrate>144)
  77. {
  78. baserefreshrate = 144;
  79. }
  80. refreshrate = baserefreshrate;
  81.  
  82. var animationduration = Math.round(1000/refreshrate);
  83. //var relativeratio = Math.round(51-smoothness/2)/100;
  84. var relativeratio = Math.round(1/(1+smoothness)*100)/100;
  85. //var relativeratio = relativeratio;
  86.  
  87.  
  88.  
  89. var lastLoop = new Date;
  90. //var lastrefreshrate = 0;
  91. function gameLoop() {
  92. var thisLoop = new Date;
  93. var refreshrate0 = 1000 / (thisLoop - lastLoop + 1);
  94. lastLoop = thisLoop;
  95. refreshrate = refreshrate + (refreshrate0-refreshrate)*0.01;
  96. refreshrate = Math.round(refreshrate);
  97. if (DEBUG)
  98. {
  99. console.log(refreshrate);
  100. }
  101. animationduration = Math.round(1000/(refreshrate));
  102. //var relativeratio = Math.round(51-smoothness/2)/100;
  103. relativeratio = Math.round(1/(1+smoothness*refreshrate/baserefreshrate)*100)/100;
  104. }
  105. gameLoop();
  106.  
  107.  
  108. function InitSmoothscroll()
  109. {
  110. LoadConfig();
  111.  
  112. InitConfigmenu();
  113.  
  114. var startposition = false;
  115. var targetposition = 0;
  116. var position = 0;
  117.  
  118. var scrollfocus = ss$('body');
  119. function hasScrollBarVisible(element)
  120. {
  121. //return (document.documentElement.scrollHeight !== document.documentElement.clientHeight);
  122. // Get the computed style of the body element
  123. var cStyle = element.currentStyle||window.getComputedStyle(element, "");
  124. // Check the overflow and overflowY properties for "auto" and "visible" values
  125. var scrollbar1 = cStyle.overflow == "scroll" || cStyle.overflowY == "scroll";
  126.  
  127. var scrollbar2 = cStyle.overflow == "auto" || cStyle.overflowY == "auto";
  128. var scrollbar = scrollbar1 || scrollbar2;
  129. return scrollbar;
  130. }
  131.  
  132.  
  133. function hasscrollbars(scrollfocus)
  134. {
  135. var hasvisiblescrollbars = hasScrollBarVisible(scrollfocus);
  136. var parentelement = ss$(scrollfocus).parent();
  137. if ( ss$(parentelement))
  138. {
  139. //if (ss$(parentelement).is("textarea") || ss$(scrollfocus).is("textarea"))
  140. if (ss$(parentelement).is("textarea") || ss$(scrollfocus).is("textarea") || ss$(parentelement).is("article") || ss$(parentelement).is("article"))
  141. {
  142. return true;
  143. }
  144. else
  145. {
  146. if (ss$(parentelement).hasClass( "yt-scrollbar" ) || ss$(scrollfocus).hasClass( "yt-scrollbar" ))
  147. {
  148. return true;
  149. }
  150. return hasvisiblescrollbars;
  151. }
  152. }
  153. else
  154. {
  155. //scrollfocus = ss$('body');
  156. //maxposition = ss$(scrollfocus).height();
  157. return hasvisiblescrollbars;
  158. }
  159. return false;
  160. }
  161.  
  162. function UpdatePosition(element)
  163. {
  164. gameLoop();
  165. var positiondelta = ss$(element)[0].getAttribute( "positiondelta" );
  166. //var smoothdelta = Math.sqrt(positiondelta*positiondelta*relativeratio);
  167. var smoothdelta = Math.sqrt(positiondelta*positiondelta*relativeratio);
  168. if (positiondelta<0)
  169. {
  170. smoothdelta = smoothdelta*(-1);
  171. }
  172. //var relative = position - ss$(element).scrollTop();
  173. //console.log("smoothdelta:" + smoothdelta);
  174. if (Math.abs( (positiondelta-smoothdelta)) <= 1 )
  175. {
  176. ss$(element).stop();
  177. ss$(element).animate({
  178. scrollTop: '+=' + Math.round(positiondelta)
  179. }, animationduration, "linear", function() {
  180. ss$(element).attr( "positiondelta",0 );
  181. ss$(element)[0].setAttribute( "positiondelta",0 );
  182. if (DEBUG)
  183. {
  184. ss$(element).css( "border", "1px solid red" );
  185. }
  186. });
  187. }
  188. else
  189. {
  190. ss$(element).stop();
  191. ss$(element).animate({
  192. scrollTop: '+=' + Math.round(smoothdelta)
  193. }, animationduration, "linear", function() {
  194. ss$(element).attr( "positiondelta",positiondelta-smoothdelta );
  195. ss$(element)[0].setAttribute("positiondelta",positiondelta-smoothdelta );
  196. UpdatePosition(element);
  197. if (DEBUG)
  198. {
  199. ss$(element).css( "border", "1px solid red" );
  200. }
  201. });
  202. }
  203. }
  204.  
  205.  
  206. function MouseScroll (e) {
  207. var mul = 1;
  208. if (!WEBKIT || alternative_sesitivity_multiplier)
  209. mul = 40;
  210. var x = e.deltaX*mul;
  211. var y = e.deltaY*mul;
  212. scrollfocus = UpdateFocus(x,y);
  213. console.log(scrollfocus);
  214. var positiondelta = 0;
  215. var lastscrolltop = 0;
  216.  
  217. var parentelement = ss$(scrollfocus).parent();
  218. if ( ss$(parentelement))
  219. {
  220. if (ss$(parentelement).is("textarea") || ss$(scrollfocus).is("textarea"))
  221. {
  222. //return true;
  223. }
  224. else
  225. {
  226. if ( ss$(parentelement).height() < ss$(scrollfocus).height())
  227. {
  228. //maxposition = ss$(scrollfocus).height() - ss$(parentelement).height();
  229. }
  230. else
  231. {
  232. if (ss$(scrollfocus).height()==0)
  233. {
  234. //scrollfocus = ss$('body');
  235. //maxposition = ss$(scrollfocus).height();
  236. }
  237.  
  238. //scrollfocus = ss$('body');
  239. //return MouseScroll (event);
  240. //return true;
  241. }
  242. }
  243. }
  244. else
  245. {
  246. //scrollfocus = ss$('html');
  247. //maxposition = ss$(scrollfocus).height();
  248. }
  249. var rolled = y;
  250. //console.log("rolled: " + rolled);
  251. //if (ss$(scrollfocus).data("positiondelta" )==undefined)
  252. //$embellishment.data("embellishmentid",1)
  253. var lastscrolltop = ss$(scrollfocus).scrollTop();
  254. ss$(scrollfocus).scrollTop(lastscrolltop+rolled);
  255. if (ss$(scrollfocus).scrollTop()==lastscrolltop)
  256. {
  257. if (!ss$(scrollfocus).is("html"))
  258. {
  259. focus = parentelement;
  260. //focus = UpdateFocus(event);
  261. //return MouseScroll (event);
  262. //console.log("false");
  263. //return false;
  264. }
  265. else
  266. {
  267. //console.log("true");
  268. //return false;
  269. }
  270. }
  271. else
  272. {
  273. e.preventDefault();
  274. }
  275. ss$(scrollfocus).scrollTop(lastscrolltop);
  276. //console.log(scrollfocus);
  277. if (ss$(scrollfocus)[0].getAttribute("positiondelta")==undefined)
  278. {
  279. positiondelta = 0;
  280. //console.log("positiondelta: undefined");
  281. }
  282. else
  283. {
  284. positiondelta = ss$(scrollfocus)[0].getAttribute("positiondelta");
  285. //console.log("positiondelta: " + positiondelta);
  286. }
  287. positiondelta = positiondelta*1;
  288. var direction = rolled/Math.abs(rolled);
  289. //var positiondeltadelta = rolled*sensitivity + Math.sqrt(Math.abs(positiondelta/rolled))*acceleration*rolled;
  290. //var positiondeltadelta = rolled*(sensitivity+Math.sqrt(Math.abs(positiondelta))*acceleration);
  291. //
  292. var positiondeltadelta = Math.round(rolled*sensitivity + Math.sqrt(Math.abs(positiondelta-rolled*sensitivity))*acceleration*rolled*0.03/sensitivity);
  293. positiondelta = positiondelta + positiondeltadelta;
  294. ss$(scrollfocus)[0].setAttribute("positiondelta",positiondelta );
  295. UpdatePosition(ss$(scrollfocus));
  296. //element.innerHTML = "";
  297. //console.log("pos:" + position);
  298. //event.preventDefault();
  299. return true;
  300. }
  301.  
  302. function canscroll(element, dir)
  303. {
  304. var scrollable = ss$(element);
  305. var lastscrolltop = ss$(scrollable).scrollTop();
  306. ss$(scrollable).scrollTop(lastscrolltop+dir);
  307. if (ss$(scrollable).scrollTop()==lastscrolltop)
  308. {
  309. ss$(scrollable).scrollTop(lastscrolltop);
  310. return false;
  311. }
  312. else
  313. {
  314. ss$(scrollable).scrollTop(lastscrolltop);
  315. return true;
  316. }
  317. }
  318.  
  319. function UpdateFocus(x,y) {
  320. /*var dir = 0;
  321. if ('wheelDelta' in event) {
  322. dir = event.wheelDelta;
  323. }
  324. else { // Firefox
  325. // The measurement units of the detail and wheelDelta properties are different.
  326. dir = event.detail*(-120);
  327. }*/
  328. var dir = y;
  329. //console.log(dir);
  330. //dir = dir*(-1);
  331. //
  332. var nodelist = document.querySelectorAll( ":hover" );
  333. ss$(nodelist).stop();
  334. //console.log(nodelist);
  335. if (WEBKIT)
  336. {
  337. scrollfocus = ss$('body');
  338. }
  339. else
  340. {
  341. scrollfocus = ss$('html');
  342. }
  343. for (var i = nodelist.length-1; i >= 0 ; i--) {
  344. //var parent = nodelist[i-1];
  345. var newfocus = nodelist[i];
  346. if (DEBUG)
  347. {
  348. ss$(newfocus).css( "border", "1px solid blue" );
  349. //var debugtimer = setTimeout(function(){ ss$(newfocus).css( "border", "0px solid white" ); }, 1000);
  350. }
  351. //if (ss$(newfocus).hasScrollBar3() && hasScrollBarVisible(newfocus) && canscroll(newfocus, dir) && hasscrollbars(newfocus))
  352. if (canscroll(newfocus, dir) && hasscrollbars(newfocus))
  353. {
  354. scrollfocus = ss$(newfocus);
  355. return newfocus;
  356. }
  357. }
  358.  
  359. return scrollfocus;
  360. }
  361. ss$('html').bind({
  362. mousewheel: function(e) {
  363. if (DEBUG)
  364. {
  365. console.log(scrollfocus);
  366. }
  367. //console.log("scrolling");
  368. //MouseScroll(e.originalEvent);
  369. },
  370.  
  371. mousedown: function(e) {
  372. if (DEBUG)
  373. {
  374. console.log(scrollfocus);
  375. }
  376. if (scrollfocus)
  377. {
  378. ss$(scrollfocus)[0].setAttribute( "positiondelta",0 );
  379. ss$(scrollfocus).stop();
  380. }
  381. //scrollfocus = UpdateFocus(e.originalEvent);
  382. //console.log("click");
  383. }
  384. });
  385. //Init(window);
  386.  
  387. /*
  388. (function(window,document) {
  389. var prefix = "", _addEventListener, onwheel, support;
  390.  
  391. // detect event model
  392. if ( window.addEventListener ) {
  393. _addEventListener = "addEventListener";
  394. } else {
  395. _addEventListener = "attachEvent";
  396. prefix = "on";
  397. }
  398.  
  399. // detect available wheel event
  400. support = "onwheel" in document.createElement("div") ? "wheel" : // Modern browsers support "wheel"
  401. document.onmousewheel !== undefined ? "mousewheel" : // Webkit and IE support at least "mousewheel"
  402. "DOMMouseScroll"; // let's assume that remaining browsers are older Firefox
  403.  
  404. window.addWheelListener = function( elem, callback, useCapture ) {
  405. _addWheelListener( elem, support, callback, useCapture );
  406.  
  407. // handle MozMousePixelScroll in older Firefox
  408. if( support == "DOMMouseScroll" ) {
  409. _addWheelListener( elem, "MozMousePixelScroll", callback, useCapture );
  410. }
  411. };
  412.  
  413. function _addWheelListener( elem, eventName, callback, useCapture ) {
  414. elem[ _addEventListener ]( prefix + eventName, support == "wheel" ? callback : function( originalEvent ) {
  415. !originalEvent && ( originalEvent = window.event );
  416.  
  417. // create a normalized event object
  418. var event = {
  419. // keep a ref to the original event object
  420. originalEvent: originalEvent,
  421. target: originalEvent.target || originalEvent.srcElement,
  422. type: "wheel",
  423. deltaMode: originalEvent.type == "MozMousePixelScroll" ? 0 : 1,
  424. deltaX: 0,
  425. deltaZ: 0,
  426. preventDefault: function() {
  427. originalEvent.preventDefault ?
  428. originalEvent.preventDefault() :
  429. originalEvent.returnValue = false;
  430. }
  431. };
  432. // calculate deltaY (and deltaX) according to the event
  433. if ( support == "mousewheel" ) {
  434. event.deltaY = - 1/40 * originalEvent.wheelDelta;
  435. // Webkit also support wheelDeltaX
  436. originalEvent.wheelDeltaX && ( event.deltaX = - 1/40 * originalEvent.wheelDeltaX );
  437. } else {
  438. event.deltaY = originalEvent.detail;
  439. }
  440.  
  441. // it's time to fire the callback
  442. return callback( event );
  443.  
  444. }, useCapture || false );
  445. }
  446.  
  447. })(window,document);
  448. */
  449. /*addWheelListener( document, function( e ) {
  450. //console.log( e.deltaY );
  451. MouseScroll(e);
  452. });*/
  453. document.documentElement.addEventListener("wheel", function(e){
  454. MouseScroll(e);
  455. //console.log("scrolling");
  456. });
  457. /*var oldpos = ss$('body').scrollTop();
  458. ss$('body').scrollTop(1);
  459. var iswebkit = ss$('body').scrollTop()>0;
  460. ss$('body').scrollTop(oldpos);*/
  461.  
  462. //WEBKIT = 'webkitRequestAnimationFrame' in window;
  463. WEBKIT = document.compatMode == 'CSS1Compat'
  464. //console.log("window: " + window);
  465. console.log("Smoothscroll initiated! Webkit: " + WEBKIT);
  466. }
  467.  
  468. var JQUERY = false;
  469. var OWNJQUERY = false;
  470. var OLDJQUERY = false;
  471.  
  472. var old$;
  473. var oldjQuery;
  474.  
  475. var ss$;
  476.  
  477. function jQueryVersion(temp$)
  478. {
  479. if (typeof temp$ == 'undefined' || typeof temp$.fn == 'undefined' || typeof temp$.fn.jquery == 'undefined')
  480. {
  481. return 0;
  482. }
  483.  
  484. var versiontable = temp$.fn.jquery.split('.');
  485. var version = 0;
  486. for (var i = versiontable.length-1; i >= 0; i--) {
  487. var power = versiontable.length-i;
  488. version += Math.pow(10,power-1)*versiontable[i];
  489. }
  490. return version;
  491. }
  492.  
  493.  
  494. function LoadConfig()
  495. {
  496. smoothness = GM_getValue( 'smoothness', smoothness );
  497. sensitivity = GM_getValue( 'sensitivity', sensitivity );
  498. acceleration = GM_getValue( 'acceleration', acceleration );
  499. baserefreshrate = GM_getValue( 'baserefreshrate', baserefreshrate );
  500. //alternative_sesitivity_multiplier = GM_getValue( 'alternative_sesitivity_multiplier', alternative_sesitivity_multiplier );
  501. console.log("Config for smoothscroll loaded!")
  502. }
  503.  
  504. function SaveConfig()
  505. {
  506. GM_setValue( 'smoothness', document.getElementById('ss-smoothness').value)
  507. GM_setValue( 'sensitivity', document.getElementById('ss-sensitivity').value)
  508. GM_setValue( 'acceleration', document.getElementById('ss-acceleration').value)
  509. GM_setValue( 'baserefreshrate', document.getElementById('ss-baserefreshrate').value)
  510. //console.log(document.getElementById('ss-alternative_sesitivity_multiplier').checked)
  511. console.log("Config for smoothscroll saved!")
  512. }
  513.  
  514. function InitConfigmenu()
  515. {
  516. var configbar = document.createElement('div');
  517. configbar.setAttribute("id","ss-configbar");
  518. 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="100" value="' + smoothness + '"></td><td> Smoothness factor value (how strong the smoothing effect is)</td></tr><tr><td>Sensitivity</td><td><input type="number" id="ss-sensitivity" min="0" max="100" value="' + sensitivity + '"></td><td> Scroll sensitivity (duh)</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 (saves your finger)</td></tr><tr><td>Refreshrate</td><td><input type="number" id="ss-baserefreshrate" min="1" max="100" value="' + baserefreshrate + '"></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>';
  519. var configparent = document.getElementsByTagName("body")[0];
  520. configbar.style.width = '100%';
  521. configbar.style.display = 'none';
  522. configbar.style.position = 'absolute';
  523. configbar.style.zIndex = '9999';
  524. configbar.style.backgroundColor = 'white';
  525. configparent.insertBefore(configbar, configparent.childNodes[0]);
  526. ss$("#ss-close").click(function() {
  527. //ss$("#ss-config").animate({
  528. // height: '0'
  529. //}, 100);
  530. ss$("#ss-configbar").hide("slow");
  531. });
  532. ss$("#ss-save").click(function() {
  533. SaveConfig();
  534. });
  535. //ss$("#ss-configbar").hide();
  536. }
  537.  
  538. function ConfigSmoothscroll()
  539. {
  540. if (typeof ss$ == 'undefined'){
  541. alert("Smoothscroll is not running properly on this page!");
  542. return;
  543. }
  544. //ss$("#ss-config").animate({
  545. // height: '100px'
  546. //}, 100);
  547. ss$("#ss-configbar").show("slow");
  548. ss$("html, body").animate({ scrollTop: 0 }, "slow");
  549. }
  550.  
  551. GM_registerMenuCommand("Configurate smoothscroll", ConfigSmoothscroll);
  552.  
  553.  
  554. function LoadjQuery(loading)
  555. {
  556. if (loading)
  557. {
  558. if (typeof $ == 'undefined' || typeof jQuery == 'undefined' || jQueryVersion($)<minimal_jquery_version)
  559. {
  560. if (jquery_retry_count<jquery_retry_max)
  561. {
  562. jquery_retry_count++;
  563. setTimeout(function() {
  564. LoadjQuery(true);
  565. }, 100);
  566. }
  567. else
  568. {
  569. console.log("Failed to load smoothscroll!");
  570. if (typeof old$ != 'undefined') {
  571. $ = old$;
  572. }
  573. if (typeof oldjQuery != 'undefined') {
  574. jQuery = oldjQuery;
  575. }
  576. }
  577. }
  578. else
  579. {
  580. ss$ = $;
  581. ssjQuery = jQuery;
  582. console.log("jQuery loaded!");
  583. if (typeof old$ != 'undefined') {
  584. $ = old$;
  585. }
  586. if (typeof oldjQuery != 'undefined') {
  587. jQuery = oldjQuery;
  588. }
  589. console.log("Page jQuery version: " + jQueryVersion(old$));
  590. console.log("Script jQuery version: " + jQueryVersion(ss$));
  591. InitSmoothscroll();
  592. }
  593. }
  594. else
  595. {
  596. console.log("Loading own jQuery...");
  597. jquery_retry_count = 0;
  598. var filename = "https://code.jquery.com/jquery-2.2.3.js";
  599. var fileref = document.createElement('script');
  600. fileref.setAttribute("type","text/javascript");
  601. fileref.setAttribute("src", filename);
  602.  
  603. if (typeof fileref!="undefined")
  604. {
  605. var scriptparent = document.getElementsByTagName("head")[0];
  606.  
  607. if (typeof scriptparent=="undefined")
  608. {
  609. var scriptparent = document.createElement('head');
  610. document.getElementsByTagName("html")[0].appendChild(scriptparent);
  611. }
  612. scriptparent.appendChild(fileref);
  613. }
  614. LoadjQuery(true);
  615. }
  616. }
  617.  
  618.  
  619. function Init()
  620. {
  621.  
  622. if (typeof ss$ == 'undefined' )
  623. {
  624. var sitejquery = !(typeof $ == 'undefined' || typeof jQuery == 'undefined');
  625. if (sitejquery && jQueryVersion($)>=minimal_jquery_version)
  626. {
  627. ss$ = $;
  628. ssjQuery = jQuery;
  629. console.log("Reusing page jQuery...");
  630. console.log("Page jQuery version: " + jQueryVersion($));
  631. InitSmoothscroll();
  632. }
  633. else
  634. {
  635. if (typeof $ != 'undefined' && typeof old$ == 'undefined')
  636. {
  637. old$ = $;
  638. }
  639. if (typeof jQuery != 'undefined' && typeof oldjQuery == 'undefined')
  640. {
  641. oldjQuery = jQuery;
  642. }
  643. LoadjQuery(false);
  644. }
  645. }
  646. }
  647.  
  648.  
  649. console.log("Loading smoothscroll...");
  650. Init();
  651. //InitSmoothscroll();