Smoothscroll

Smooth scrolling on pages using javascript

当前为 2019-05-19 提交的版本,查看 最新版本

  1. // ==UserScript==
  2. // @name Smoothscroll
  3. // @author Creec Winceptor
  4. // @description Smooth scrolling on pages using javascript
  5. // @namespace https://greasyfork.org/users/3167
  6. // @include *
  7. // @version 9.5
  8. // ==/UserScript==
  9.  
  10. var Smoothscroll = {};
  11.  
  12.  
  13. //settings
  14. Smoothscroll.Smoothness = 0.5;
  15. Smoothscroll.Acceleration = 0.5;
  16.  
  17.  
  18. //debug
  19. Smoothscroll.Debug = false;
  20. //autodetected
  21. Smoothscroll.Refreshrate = 60;
  22.  
  23. //scrolling and animation
  24. function ScrollSubpixels(element, newvalue)
  25. {
  26. if (newvalue!=undefined)
  27. {
  28. element.scrollsubpixels = newvalue;
  29. return newvalue;
  30. }
  31. else
  32. {
  33. var olddelta = element.scrollsubpixels;
  34. if (olddelta!=undefined)
  35. {
  36. return olddelta;
  37. }
  38. return 0;
  39. }
  40. }
  41. function ScrollPixels(element, newvalue)
  42. {
  43. if (newvalue!=undefined)
  44. {
  45. element.scrollpixels = newvalue;
  46. ScrollSubpixels(element, 0);
  47. return newvalue;
  48. }
  49. else
  50. {
  51. var olddelta = element.scrollpixels;
  52. if (olddelta!=undefined)
  53. {
  54. return olddelta;
  55. }
  56. return 0;
  57. }
  58. }
  59.  
  60. var last = 0;
  61. function AnimateScroll(target, now) {
  62. var scrollsubpixels = ScrollSubpixels(target);
  63. var scrollpixels = ScrollPixels(target);
  64.  
  65. var scrolldirection = 0;
  66. if (scrollpixels>0) {
  67. scrolldirection = 1;
  68. }
  69. if (scrollpixels<0) {
  70. scrolldirection = -1;
  71. }
  72.  
  73. var scrollratio = 1-Math.pow( Smoothscroll.Refreshrate, -1/(Smoothscroll.Refreshrate*Smoothscroll.Smoothness));
  74. var scrollrate = scrollpixels*scrollratio;
  75. if (Math.abs(scrollpixels)>1) {
  76. var fullscrolls = Math.floor(Math.abs(scrollrate))*scrolldirection;
  77. var scrollsubpixelsadded = scrollrate - fullscrolls;
  78.  
  79. var additionalscrolls = Math.floor(Math.abs(scrollsubpixels + scrollsubpixelsadded))*scrolldirection;
  80. var scrollsubpixelsleft = scrollsubpixels + scrollsubpixelsadded - additionalscrolls;
  81.  
  82. ScrollPixels(target, scrollpixels-fullscrolls-additionalscrolls);
  83. ScrollSubpixels(target, scrollsubpixelsleft);
  84. target.scrollTop += fullscrolls + additionalscrolls;
  85.  
  86. target.scrollanimated = true;
  87. window.requestAnimationFrame(function() {
  88. AnimateScroll(target);
  89. });
  90. } else
  91. {
  92. window.requestAnimationFrame(function() {
  93. ScrollPixels(target, 0);
  94. });
  95. target.scrollanimated = false;
  96. }
  97. }
  98.  
  99. Smoothscroll.Stop = function(target) {
  100. if (target) {
  101. ScrollPixels(target, 0);
  102. }
  103. }
  104. Smoothscroll.Start = function(target, scrollamount) {
  105. if (target) {
  106. var scrollpixels = ScrollPixels(target);
  107.  
  108. ScrollPixels(target, scrollamount);
  109. if (!target.scrollanimated) {
  110. AnimateScroll(target);
  111. }
  112. }
  113. }
  114.  
  115. if (typeof module !== 'undefined') {
  116. module.exports = Smoothscroll;
  117. }
  118.  
  119.  
  120. function CanScroll(element, dir) {
  121.  
  122. if (dir<0)
  123. {
  124. return element.scrollTop>0;
  125. }
  126. if (dir>0)
  127. {
  128. if (element==document.body) {
  129. if (element.scrollTop==0) {
  130. element.scrollTop = 3;
  131. if (element.scrollTop==0) {
  132. return false;
  133. }
  134. element.scrollTop = 0;
  135. }
  136. return Math.round(element.clientHeight+element.scrollTop)<(element.offsetHeight);
  137. }
  138. return Math.round(element.clientHeight+element.scrollTop)<(element.scrollHeight);
  139. }
  140. }
  141. function HasScrollbar(element)
  142. {
  143. //TODO: problem with webkit, body not scrollable?
  144. if (element==window || element==document) {
  145. return false;
  146. }
  147. if (element==document.body) {
  148. return window.getComputedStyle(document.body)['overflow-y']!="hidden";
  149. }
  150. //THANK YOU TO: https://tylercipriani.com/blog/2014/07/12/crossbrowser-javascript-scrollbar-detection/
  151. if (element==document.documentElement) {
  152. return window.innerWidth > document.documentElement.clientWidth;
  153. } else {
  154. //return (element.clientWidth-element.clientWidth)>0;
  155. var style = window.getComputedStyle(element);
  156. return style['overflow-y']!="hidden" && style['overflow-y']!="visible";
  157. }
  158.  
  159. }
  160.  
  161. function Scrollable(element, dir)
  162. {
  163. //TODO: problem with webkit, body not scrollable?
  164. if (element==document.body) {
  165. //return false;
  166. }
  167. var scrollablecheck = CanScroll(element, dir);
  168. if (!scrollablecheck) {
  169. if (Smoothscroll.Debug) {
  170. console.log("scrollablecheck: " + scrollablecheck);
  171. }
  172. return false;
  173. }
  174. var scrollbarcheck = HasScrollbar(element);
  175. if (!scrollbarcheck) {
  176. if (Smoothscroll.Debug) {
  177. console.log("scrollbarcheck: " + scrollbarcheck);
  178. }
  179. return false;
  180. }
  181.  
  182. if (Smoothscroll.Debug) {
  183. console.log("scrollablecheck: " + scrollablecheck);
  184. console.log("scrollbarcheck: " + scrollbarcheck);
  185. }
  186. return true;
  187. }
  188. function GetPath(e) {
  189. if (e.path) {
  190. return e.path;
  191. }
  192. if (e.composedPath) {
  193. return e.composedPath();
  194. }
  195. if (Smoothscroll.Debug) {
  196. console.log("Smoothscroll: e.path is undefined");
  197. }
  198. return null;
  199. }
  200.  
  201. function GetTarget(e) {
  202. var direction = e.deltaY;
  203. var nodes = GetPath(e);
  204. if (!nodes) {
  205. return null;
  206. }
  207. if (Smoothscroll.Debug) {
  208. console.log("nodes: ");
  209. console.log(nodes);
  210. console.log("target: ");
  211. }
  212. for (var i=0; i<(nodes.length); i++) {
  213. var node = nodes[i];
  214. if (Smoothscroll.Debug) {
  215. console.log(node);
  216. }
  217. if (Scrollable(node, direction))
  218. {
  219. if (Smoothscroll.Debug) {
  220. console.log("true");
  221. }
  222. return node;
  223. }
  224. }
  225. if (Smoothscroll.Debug) {
  226. console.log("false");
  227.  
  228. }
  229.  
  230. return null;
  231. }
  232.  
  233. function GetStyleProperty(el, styleprop){
  234. if(window.getComputedStyle){
  235. var heightprop = document.defaultView.getComputedStyle(el, null).getPropertyValue(styleprop);
  236. if (heightprop) {
  237. return parseInt(heightprop);
  238. }
  239. }
  240. else if(el.currentStyle){
  241. var heightprop = el.currentStyle[styleprop.encamel()];
  242. if (heightprop) {
  243. return parseInt(heightprop);
  244. }
  245. }
  246. return null;
  247. }
  248.  
  249. //mouse event scroll handlers
  250. function StopScroll(e) {
  251. var nodes = GetPath(e);
  252. if (!nodes) {
  253. return null;
  254. }
  255.  
  256. for (var i=0; i<(nodes.length); i++) {
  257. var node = nodes[i];
  258. Smoothscroll.Stop(node);
  259. }
  260. }
  261. function StartScroll(e, target) {
  262.  
  263. if (e.defaultPrevented)
  264. {
  265. return true;
  266. }
  267. else
  268. {
  269. var delta = e.deltaY;
  270.  
  271. if (e.deltaMode && e.deltaMode==1) {
  272. var line = GetStyleProperty(target, 'line-height');
  273. if (line && line>0) {
  274. delta = e.deltaY * line;
  275. }
  276. }
  277.  
  278. if (e.deltaMode && e.deltaMode==2) {
  279. var page = target.clientHeight;
  280. if (page && page>0) {
  281. delta = e.deltaY * page;
  282. }
  283. }
  284. var scrollpixels = ScrollPixels(target);
  285.  
  286. var accelerationratio = Math.sqrt(Math.abs(scrollpixels/delta*Smoothscroll.Acceleration));
  287.  
  288. var acceleration = Math.round(delta*accelerationratio);
  289.  
  290. Smoothscroll.Start(target, scrollpixels + delta + acceleration);
  291.  
  292. e.preventDefault();
  293. }
  294. }
  295.  
  296. //mouse event call handlers
  297. function WheelEvent(e) {
  298. var target = GetTarget(e);
  299.  
  300. if (target) {
  301. StartScroll(e, target);
  302. }
  303. }
  304. function ClickEvent(e) {
  305. StopScroll(e);
  306. }
  307.  
  308. var now0 = null;
  309. function Fps(now) {
  310. if (now0 != null) {
  311. Smoothscroll.Refreshrate = 1000 / (now - now0);
  312. }
  313. now0 = now;
  314.  
  315. window.requestAnimationFrame(Fps);
  316. };
  317.  
  318. //init function
  319. function Init()
  320. {
  321.  
  322. if (window.top != window.self) {
  323. //console.log("Smoothscroll: ignoring iframe");
  324. return null;
  325. }
  326. if (window.Smoothscroll && window.Smoothscroll.Loaded) {
  327. //console.log("Smoothscroll: already loaded");
  328. return null;
  329. }
  330.  
  331. if (!window.requestAnimationFrame) {
  332. window.requestAnimationFrame =
  333. window.mozRequestAnimationFrame ||
  334. window.webkitRequestAnimationFrame;
  335. }
  336.  
  337. document.documentElement.addEventListener("wheel", function(e){
  338. WheelEvent(e);
  339. if (Smoothscroll.Debug) {
  340. console.log(e);
  341. }
  342. },{ passive: false });
  343.  
  344. document.documentElement.addEventListener("mousedown", function(e){
  345. ClickEvent(e);
  346. if (Smoothscroll.Debug) {
  347. console.log(e);
  348. }
  349. });
  350. window.Smoothscroll = Smoothscroll;
  351. window.Smoothscroll.Loaded = true;
  352. window.requestAnimationFrame(Fps);
  353. console.log("Smoothscroll: loaded");
  354. }
  355. Init();