Auto scroll keys [adopted]

Read articles hands-free! Pres Ctrl-Down or Opt-Down to start slowly scrolling the page. Press again to adjust speed. Escape or Space to stop.

  1. // ==UserScript==
  2. // @name Auto scroll keys [adopted]
  3. // @namespace http://userscripts.org/users/44573
  4. // @version 1.1.4
  5. // @license MIT
  6. // @description Read articles hands-free! Pres Ctrl-Down or Opt-Down to start slowly scrolling the page. Press again to adjust speed. Escape or Space to stop.
  7. // @grant none
  8. // @include *
  9. // ==/UserScript==
  10.  
  11. // This script is a derivative of http://userscripts-mirror.org/scripts/show/70300 by PaC1250
  12.  
  13. // Options:
  14.  
  15. var initialSpeed = 10; // Pixels per second
  16.  
  17. var maxPerSecond = 60; // Target FPS
  18.  
  19. var resetThreshold = 10; // If the user scrolls the page manually by more pixels than this value, then we will start scrolling from the new position.
  20.  
  21. // When zoomed in, a page pixel will be larger than a screen pixel. Enabling this will shift by fractions of a page pixel, rather than whole page pixels, so that the text won't make multi-pixel jumps.
  22. var attemptSubPixelScrolling = false;
  23.  
  24. // But at default zoom, sub-pixel scrolling may look a little bit jittery, so this option will enable sub-pixel zooming only when it's needed.
  25. // (Although personally in Chrome I found sub-pixel zooming looked preferable even at zoom 100%.)
  26. // This overwrites the value of attemptSubPixelScrolling when scrolling begins.
  27. var attemptSubPixelScrollingOnlyIfZoomedIn = true;
  28.  
  29. // The user may perform normal scrolling actions during auto-scroll (e.g. by pressing Up or PageUp or using the scroll bar). We will detect and ackowledge these (update realy) if we see a difference of more than this many pixels.
  30. // If the threshold is set too low, the script's own scrolling will trigger it, especially on slower machines or under heavy load.
  31. // But if set too high, the threshold may fail to fire if, when the user presses Up to scroll back a little, the browser performs that with a smooth scroll of lots of small movements, rather than one larger jump.
  32.  
  33. // BUG: When zoomed in, jumping by a whole (unzoomed) pixel it too coarse, because we see the text move by more than one screen pixel. But we cannot control that: we give the browser a float for scrolling but it rounds it to an int!
  34. // A possible workaround would be to perform some scrolling (perhaps just the remainder part before rounding) as a transform on the page. This might not work in all browsers.
  35.  
  36. // 2012/10/09 Now runs at 60fps or whatever the machine can handle
  37.  
  38. // Constants:
  39.  
  40. var DOM_VK_DOWN = 40;
  41. var DOM_VK_UP = 38;
  42. var DOM_VK_ESCAPE = 27;
  43. var DOM_VK_SPACE = 32;
  44. var DOM_VK_ENTER = 13;
  45.  
  46. // Runtime:
  47.  
  48. var u44573_go = false;
  49. var scrollSpeed = 0;
  50.  
  51. var lastTime;
  52. var realx, realy; // We store scroll position as floats; basing scrolling on a float is smoother than the browser's int-rounded scrollTop. But we may need to keep realy in sync with scrollTop, if the user changes the later during auto-scroll.
  53.  
  54. window.addEventListener('keydown', u44573_handler, true);
  55.  
  56. var transformBeforeScrolling;
  57.  
  58. function u44573_handler(e) {
  59. var change = 0.60; // Probably could be lowered a bit if we make scrollSpeed truly analogue.
  60. var upDelta, downDelta; // The acceleration of each key
  61.  
  62. if (scrollSpeed === 0 || !u44573_go) {
  63. upDelta = initialSpeed;
  64. downDelta = initialSpeed;
  65. } else {
  66. if (Math.abs(scrollSpeed) < 0.1) {
  67. scrollSpeed = 0.1 * sgn(scrollSpeed);
  68. }
  69. downDelta = Math.abs(scrollSpeed)*change;
  70. upDelta = Math.abs(scrollSpeed)*change/(1+change);
  71. if (scrollSpeed < 0) {
  72. var tmpDelta = downDelta;
  73. downDelta = upDelta;
  74. upDelta = tmpDelta;
  75. }
  76. }
  77.  
  78. // Scroll downwards with CTRL-Down_Arrow or Opt-Down_Arrow on Mac
  79. if((e.altKey || e.ctrlKey) && e.keyCode == DOM_VK_DOWN) {
  80. scrollSpeed += downDelta;
  81. e.preventDefault();
  82. }
  83. // Scroll upwards with CTRL-Up_Arrow or Opt-Up_Arrow on Mac
  84. if((e.altKey || e.ctrlKey) && e.keyCode == DOM_VK_UP) {
  85. scrollSpeed -= upDelta;
  86. e.preventDefault();
  87. }
  88.  
  89. if(!u44573_go && scrollSpeed != 0) {
  90. startScroller();
  91. }
  92.  
  93. // Stop (ESCAPE or ENTER or SPACE)
  94. if (e.keyCode == DOM_VK_ESCAPE || e.keyCode == DOM_VK_ENTER || e.keyCode == DOM_VK_SPACE) {
  95. if (u44573_go) {
  96. u44573_go = false;
  97. scrollSpeed = 0;
  98.  
  99. if (attemptSubPixelScrolling) {
  100. document.body.style.transform = transformBeforeScrolling;
  101. }
  102.  
  103. // Do not pass keydown event to page:
  104. e.preventDefault(); // Most browsers
  105. return false; // IE
  106. }
  107. }
  108. }
  109.  
  110. function sgn(x) {
  111. return ( x>0 ? +1 : x<0 ? -1 : 0 );
  112. }
  113.  
  114. var abs = Math.abs;
  115.  
  116. function startScroller() {
  117. u44573_go = true;
  118. var s = u44573_getScrollPosition();
  119. realx = s[0];
  120. realy = s[1];
  121. lastTime = new Date().getTime();
  122. transformBeforeScrolling = document.body.style.transform;
  123.  
  124. if (attemptSubPixelScrollingOnlyIfZoomedIn) {
  125. // This technique find the zoom level in Chrome
  126. // For other browser, see https://github.com/tombigel/detect-zoom or http://stackoverflow.com/questions/1713771
  127. var screenCssPixelRatio = window.outerWidth / window.innerWidth;
  128. console.log("Detected zoom: "+screenCssPixelRatio);
  129. var isZoomedIn = (screenCssPixelRatio >= 1.05);
  130. attemptSubPixelScrolling = isZoomedIn;
  131. }
  132.  
  133. u44573_goScroll();
  134. }
  135.  
  136. function queueNextFrame(callback, duration) {
  137. if (typeof window.requestAnimationFrame === 'function') {
  138. window.requestAnimationFrame(callback);
  139. } else {
  140. setTimeout(callback, duration);
  141. }
  142. }
  143.  
  144. function u44573_goScroll() {
  145.  
  146. if (u44573_go) {
  147.  
  148. // Check if the user has scrolled the page with a key since we last scrolled.
  149. // If so, update our realx,realy.
  150. // BUG: Argh the check isn't working in Firefox 90% of the time!
  151. // Hold down the key to beat those odds.
  152. var s = u44573_getScrollPosition();
  153. if ( abs(s[0]-realx) > resetThreshold || abs(s[1]-realy) > resetThreshold ) {
  154. realx = s[0];
  155. realy = s[1];
  156. }
  157.  
  158. var timeNow = new Date().getTime();
  159. var elapsed = timeNow - lastTime;
  160. var jumpPixels = abs(scrollSpeed) * elapsed/1000;
  161. var timeToNext = 1000/maxPerSecond;
  162.  
  163. // Rather than running at a fixed FPS, at slow speeds the approaches below would delay the next callback until it was time to move to the next pixel. This was more efficient on slow machines.
  164. /*
  165. // The browser can only jump a whole number of pixels, and it rounds down.
  166. // We had to do the following anyway for jumpPixels<1 but by doing it for
  167. // small numbers (<5) we workaround the analogue/digital bug. (5*1.2=6)
  168. if (jumpPixels < 3) {
  169. timeToNext /= jumpPixels;
  170. // jumpPixels /= jumpPixels;
  171. jumpPixels = 1;
  172. }
  173. */
  174. /*
  175. var timeToNext = 1000/abs(scrollSpeed);
  176. if (timeToNext < 1000/maxPerSecond) {
  177. jumpPixels = abs(scrollSpeed)/maxPerSecond;
  178. timeToNext = 1000/maxPerSecond;
  179. } else {
  180. jumpPixels = 1;
  181. }
  182. */
  183.  
  184. realy += jumpPixels*sgn(scrollSpeed);
  185.  
  186. //var inty = Math.round(realy);
  187. var inty = Math.floor(realy);
  188.  
  189. if (attemptSubPixelScrolling) {
  190. var remaindery = realy - inty;
  191. var transform = transformBeforeScrolling + " translate(0px, "+(-remaindery)+"px)";
  192. document.body.style.transform = transform;
  193. }
  194.  
  195. window.scroll(realx, inty); // Leave it to browser to round real values to ints
  196.  
  197. lastTime = timeNow;
  198.  
  199. if (scrollSpeed == 0) {
  200. u44573_go = false;
  201. } else {
  202. queueNextFrame(u44573_goScroll, timeToNext);
  203. }
  204.  
  205. }
  206.  
  207. }
  208.  
  209. function u44573_getScrollPosition() {
  210. return Array((document.documentElement && document.documentElement.scrollLeft) || window.pageXOffset || self.pageXOffset || document.body.scrollLeft,(document.documentElement && document.documentElement.scrollTop) || window.pageYOffset || self.pageYOffset || document.body.scrollTop);
  211. }
  212.  

QingJ © 2025

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