Smoothscroll

Smooth scrolling on pages using javascript

目前為 2019-05-26 提交的版本,檢視 最新版本

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