Smooth Scrolling

Smoothly scrolls the page when a button is held

  1. // ==UserScript==
  2. // @name Smooth Scrolling
  3. // @description Smoothly scrolls the page when a button is held
  4. // @version 1.1.2
  5. // @author sllypper
  6. // @homepage https://gf.qytechs.cn/en/users/55535-sllypper
  7. // @namespace https://gf.qytechs.cn/en/users/55535-sllypper
  8. // @match *://forums.spacebattles.com/*
  9. // @match *://forums.sufficientvelocity.com/*
  10. // @match *://forum.questionablequesting.com/*
  11. // @match *://fanfiction.net/*
  12. // @match *://archiveofourown.org/*
  13. // @match *://turb0translation.blogspot.com/*
  14. // @match *://fiction.live/*
  15. // @grant none
  16. // @todo make keybinds easily configurable
  17. // ==/UserScript==
  18.  
  19. (function() {
  20. 'use strict';
  21.  
  22. // Smooth Scrolling Settings
  23.  
  24. // how much it scrolls every time (in pixels)
  25. let scrollAmount = 16;
  26.  
  27. // how long between ticks (in ms)
  28. // 16.66667 = 60 frames per second
  29. // not used if experimental is true
  30. let scrollPeriod = 16.66667;
  31.  
  32. // how much holding Shift will multiply the scrollAmount
  33. let shiftSpeedMod = 2.0;
  34.  
  35. //
  36.  
  37. // Experimental settings
  38.  
  39. // Use the alternative experimental Scroller
  40. // it's bound to your screen framerate
  41. // is supposed to be smoother
  42. let experimental = true;
  43.  
  44. // Scroll Speed in times per second
  45. // set to 0 to match your refresh rate (smooth perfection)
  46. // values above your framerate scrolls every frame
  47. let fps = 0
  48.  
  49. // programming magic stuff
  50.  
  51. const states = {
  52. NONE: 0,
  53. UP: 1,
  54. SHIFTUP: 2,
  55. DOWN: 3,
  56. SHIFTDOWN: 4
  57. }
  58. let scrollState = states.NONE;
  59. let currScrollAction = null;
  60. let isTyping = false;
  61.  
  62. document.addEventListener('focus', function(evt) {
  63. var target = evt.target;
  64. if((target.nodeName === 'INPUT' && target.type === 'text') || target.nodeName === 'TEXTAREA') isTyping = true;
  65. // else isTyping = false;
  66. }, true);
  67.  
  68. document.addEventListener('blur', function(evt) {
  69. var target = evt.target;
  70. if((target.nodeName === 'INPUT' && target.type === 'text') || target.nodeName === 'TEXTAREA') isTyping = false;
  71. }, true);
  72.  
  73. document.addEventListener("keydown", event => {
  74. // ignore keybindings when text input is focused
  75. if (isTyping || event.isComposing || event.target.getAttribute('medium-editor') != null || event.target.getAttribute('contenteditable') != null) {
  76. //console.log('entered first if')
  77. event.stopPropagation();
  78. return;
  79. }
  80. switch (event.code) {
  81. case "KeyW":
  82. case "KeyK":
  83. case "ArrowUp": {
  84. event.preventDefault();
  85. if (scrollState !== states.UP && !event.shiftKey) {
  86. //event.preventDefault();
  87. clearScrollAction();
  88. scrollState = states.UP;
  89. scrollAction(-scrollAmount);
  90. } else if (scrollState !== states.SHIFTUP && event.shiftKey) {
  91. //event.preventDefault();
  92. clearScrollAction();
  93. scrollState = states.SHIFTUP;
  94. scrollAction(-scrollAmount*shiftSpeedMod);
  95. }
  96. break;
  97. }
  98. case "KeyS":
  99. case "KeyJ":
  100. case "ArrowDown": {
  101. event.preventDefault();
  102. if (scrollState !== states.DOWN && !event.shiftKey) {
  103. // event.preventDefault();
  104. clearScrollAction();
  105. scrollState = states.DOWN;
  106. scrollAction(scrollAmount);
  107. } else if (scrollState !== states.SHIFTDOWN && event.shiftKey) {
  108. // event.preventDefault();
  109. clearScrollAction();
  110. scrollState = states.SHIFTDOWN;
  111. scrollAction(scrollAmount*shiftSpeedMod);
  112. }
  113. break;
  114. }
  115. }
  116. });
  117.  
  118. document.addEventListener("keyup", event => {
  119. switch(event.code) {
  120. case 'KeyW':
  121. case 'KeyK':
  122. case 'KeyJ':
  123. case 'KeyS':
  124. case 'ArrowDown':
  125. case 'ArrowUp':
  126. clearScrollAction();
  127. break
  128. default:
  129. // using even.key for any Shift Key
  130. if (event.key === "Shift") {
  131. clearScrollAction();
  132. }
  133. break;
  134. }
  135. });
  136.  
  137. function scrollAction(amount) {
  138. if (experimental) {
  139. scroller.move(amount)
  140. return;
  141. }
  142.  
  143. currScrollAction = setInterval(() => {
  144. window.scrollBy(0, amount);
  145. }, scrollPeriod)
  146. }
  147.  
  148. function clearScrollAction() {
  149. clearInterval(currScrollAction);
  150. currScrollAction = null;
  151. scrollState = states.NONE;
  152. scroller.stop();
  153. }
  154.  
  155. // experimental bit
  156.  
  157. let scroller = new Scroller(fps)
  158.  
  159. function Scroller(fps) {
  160.  
  161. var delay,
  162. time,
  163. frame,
  164. tref,
  165. amount;
  166.  
  167. function loop(timestamp) {
  168. if (fps !== 0) {
  169. // Scroll with fps behavior
  170. if (time === null) {time = timestamp; timestamp = 0}
  171. var seg = Math.floor((timestamp - time) / delay);
  172. if (seg > frame) {
  173. frame = seg;
  174. window.scrollBy(0, amount);
  175. }
  176. } else {
  177. // Scroll every frame behavior
  178. window.scrollBy(0, amount);
  179. }
  180. tref = requestAnimationFrame(loop)
  181. }
  182.  
  183. this.move = function(pixels) {
  184. amount = pixels;
  185. delay = 1000 / fps;
  186. frame = -1;
  187. time = null;
  188. tref = requestAnimationFrame(loop);
  189. }
  190.  
  191. this.stop = function() {
  192. cancelAnimationFrame(tref);
  193. };
  194. }
  195.  
  196. })();

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址