Web Speed Controller

control the speed of website timers and animations, aka speed up all javascript

当前为 2025-02-17 提交的版本,查看 最新版本

  1. // ==UserScript==
  2. // @name Web Speed Controller
  3. // @namespace http://tampermonkey.net/
  4. // @version 1.8
  5. // @description control the speed of website timers and animations, aka speed up all javascript
  6. // @author Minoa
  7. // @match *://*/*
  8. // @grant none
  9. // @license MIT
  10. // ==/UserScript==
  11.  
  12.  
  13. (function() {
  14. 'use strict';
  15.  
  16. // Create UI elements
  17. const controls = document.createElement('div');
  18. controls.style.cssText = `
  19. position: fixed;
  20. top: 13px;
  21. right: 18px;
  22. background: rgba(15, 23, 42, 0.8);
  23. padding: 4px;
  24. border: 1px solid rgba(255, 255, 255, 0.08);
  25. border-radius: 8px;
  26. z-index: 9999999;
  27. display: flex;
  28. gap: 4px;
  29. box-shadow: 0 4px 16px rgba(0, 0, 0, 0.22);
  30. font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Arial, sans-serif;
  31. align-items: center;
  32. transition: all 0.3s ease;
  33. width: 45px;
  34. overflow: hidden;
  35. `;
  36.  
  37. const input = document.createElement('input');
  38. input.type = 'number';
  39. input.step = '1';
  40. input.value = '1';
  41. input.style.cssText = `
  42. width: 22px;
  43. height: 22px;
  44. background: rgba(30, 41, 59, 0.8);
  45. border: 1px solid rgba(148, 163, 184, 0.1);
  46. color: rgba(226, 232, 240, 0.6);
  47. border-radius: 6px;
  48. padding: 2px;
  49. font-size: 12px;
  50. font-weight: 500;
  51. text-align: center;
  52. outline: none;
  53. transition: all 0.3s ease;
  54. -moz-appearance: textfield;
  55. cursor: pointer;
  56. `;
  57.  
  58. // Remove spinner arrows
  59. input.style.cssText += `
  60. &::-webkit-outer-spin-button,
  61. &::-webkit-inner-spin-button {
  62. -webkit-appearance: none;
  63. margin: 0;
  64. }
  65. `;
  66.  
  67. const toggleButton = document.createElement('button');
  68. toggleButton.textContent = '▶';
  69. toggleButton.style.cssText = `
  70. background: rgba(59, 130, 246, 0.5);
  71. color: rgba(255, 255, 255, 0.6);
  72. border: none;
  73. border-radius: 6px;
  74. width: 20px;
  75. height: 20px;
  76. font-size: 10px;
  77. font-weight: 600;
  78. cursor: pointer;
  79. transition: all 0.3s ease;
  80. display: none;
  81. align-items: center;
  82. justify-content: center;
  83. text-shadow: 0 1px 2px rgba(0, 0, 0, 0.1);
  84. white-space: nowrap;
  85. padding: 0;
  86. `;
  87.  
  88. let isExpanded = false;
  89. let isEnabled = false;
  90.  
  91. // Hover effect for small state
  92. controls.addEventListener('mouseenter', () => {
  93. if (!isExpanded) {
  94. controls.style.background = 'rgba(15, 23, 42, 0.45)';
  95. input.style.background = 'rgba(30, 41, 59, 0.5)';
  96. input.style.color = 'rgba(226, 232, 240, 0.8)';
  97. }
  98. });
  99.  
  100. controls.addEventListener('mouseleave', () => {
  101. if (!isExpanded) {
  102. controls.style.background = 'rgba(15, 23, 42, 0.25)';
  103. input.style.background = 'rgba(30, 41, 59, 0.3)';
  104. input.style.color = 'rgba(226, 232, 240, 0.6)';
  105. }
  106. });
  107.  
  108. function expandControls() {
  109. if (!isExpanded) {
  110. controls.style.width = 'auto';
  111. controls.style.padding = '16px';
  112. controls.style.background = 'rgba(15, 23, 42, 0.85)';
  113. controls.style.backdropFilter = 'blur(10px)';
  114. controls.style.webkitBackdropFilter = 'blur(10px)';
  115. controls.style.borderRadius = '12px';
  116. controls.style.gap = '12px';
  117. controls.style.boxShadow = '0 8px 32px rgba(0, 0, 0, 0.2)';
  118. controls.style.border = '1px solid rgba(255, 255, 255, 0.1)';
  119.  
  120. input.style.width = '70px';
  121. input.style.height = '36px';
  122. input.style.padding = '4px 8px';
  123. input.style.fontSize = '14px';
  124. input.style.background = 'rgba(30, 41, 59, 0.8)';
  125. input.style.borderRadius = '8px';
  126. input.style.border = '2px solid rgba(148, 163, 184, 0.2)';
  127. input.style.color = '#e2e8f0';
  128.  
  129. toggleButton.style.display = 'flex';
  130. toggleButton.style.width = '90px';
  131. toggleButton.style.height = '36px';
  132. toggleButton.style.padding = '8px 16px';
  133. toggleButton.style.fontSize = '14px';
  134. toggleButton.style.borderRadius = '8px';
  135. toggleButton.textContent = 'Enable';
  136. toggleButton.style.background = '#3b82f6';
  137. toggleButton.style.color = '#ffffff';
  138.  
  139. isExpanded = true;
  140. }
  141. }
  142.  
  143. function adjustInputWidth() {
  144. if (isExpanded) {
  145. const span = document.createElement('span');
  146. span.style.cssText = `
  147. position: absolute;
  148. top: -9999px;
  149. font: ${window.getComputedStyle(input).font};
  150. padding: ${window.getComputedStyle(input).padding};
  151. `;
  152. span.textContent = input.value;
  153. document.body.appendChild(span);
  154. const newWidth = Math.max(70, span.offsetWidth + 24);
  155. input.style.width = `${newWidth}px`;
  156. document.body.removeChild(span);
  157. }
  158. }
  159.  
  160. // Expand on input focus
  161. input.addEventListener('focus', () => {
  162. expandControls();
  163. input.style.borderColor = '#3b82f6';
  164. input.style.boxShadow = '0 0 0 3px rgba(59, 130, 246, 0.3)';
  165. });
  166.  
  167. input.addEventListener('blur', () => {
  168. if (isExpanded) {
  169. input.style.borderColor = 'rgba(148, 163, 184, 0.2)';
  170. input.style.boxShadow = 'none';
  171. }
  172. input.value = Math.round(input.value) || 1;
  173. });
  174.  
  175. // Handle input changes
  176. input.addEventListener('input', () => {
  177. input.value = Math.round(input.value);
  178. adjustInputWidth();
  179. });
  180.  
  181. // Keyboard navigation
  182. input.addEventListener('keydown', (e) => {
  183. const currentValue = parseInt(input.value) || 1;
  184. if (e.key === 'ArrowUp') {
  185. e.preventDefault();
  186. const newValue = currentValue + 1;
  187. input.value = newValue;
  188. adjustInputWidth();
  189. if (isEnabled) updateSpeed();
  190. } else if (e.key === 'ArrowDown') {
  191. e.preventDefault();
  192. const newValue = Math.max(1, currentValue - 1);
  193. input.value = newValue;
  194. adjustInputWidth();
  195. if (isEnabled) updateSpeed();
  196. }
  197. });
  198.  
  199. // Button styling
  200. toggleButton.addEventListener('mouseover', () => {
  201. if (isExpanded) {
  202. toggleButton.style.background = isEnabled ? '#dc2626' : '#2563eb';
  203. toggleButton.style.transform = 'translateY(-1px)';
  204. }
  205. });
  206.  
  207. toggleButton.addEventListener('mouseout', () => {
  208. if (isExpanded) {
  209. toggleButton.style.background = isEnabled ? '#ef4444' : '#3b82f6';
  210. toggleButton.style.transform = 'translateY(0)';
  211. }
  212. });
  213.  
  214. // Store original timing functions
  215. const original = {
  216. setTimeout: window.setTimeout.bind(window),
  217. setInterval: window.setInterval.bind(window),
  218. requestAnimationFrame: window.requestAnimationFrame.bind(window),
  219. dateNow: Date.now.bind(Date),
  220. originalDate: Date
  221. };
  222.  
  223. let speedMultiplier = 1;
  224. let startTime = original.dateNow();
  225. let modifiedTime = startTime;
  226.  
  227. function updateSpeed() {
  228. speedMultiplier = parseInt(input.value) || 1;
  229. modifiedTime = original.dateNow();
  230. startTime = original.dateNow();
  231. applySpeedMultiplier();
  232. }
  233.  
  234. function applySpeedMultiplier() {
  235. window.setTimeout = function(callback, delay, ...args) {
  236. return original.setTimeout(callback, delay / speedMultiplier, ...args);
  237. };
  238.  
  239. window.setInterval = function(callback, delay, ...args) {
  240. return original.setInterval(callback, delay / speedMultiplier, ...args);
  241. };
  242.  
  243. window.requestAnimationFrame = function(callback) {
  244. return original.requestAnimationFrame((timestamp) => {
  245. const adjustedTimestamp = timestamp * speedMultiplier;
  246. callback(adjustedTimestamp);
  247. });
  248. };
  249.  
  250. function TimeWarpDate(...args) {
  251. if (args.length === 0) {
  252. const now = original.dateNow();
  253. const timePassed = now - startTime;
  254. const adjustedTime = modifiedTime + (timePassed * speedMultiplier);
  255. return new original.originalDate(adjustedTime);
  256. }
  257. return new original.originalDate(...args);
  258. }
  259.  
  260. TimeWarpDate.prototype = original.originalDate.prototype;
  261.  
  262. TimeWarpDate.now = function() {
  263. const now = original.dateNow();
  264. const timePassed = now - startTime;
  265. return modifiedTime + (timePassed * speedMultiplier);
  266. };
  267.  
  268. TimeWarpDate.parse = original.originalDate.parse;
  269. TimeWarpDate.UTC = original.originalDate.UTC;
  270.  
  271. window.Date = TimeWarpDate;
  272. }
  273.  
  274. function restoreOriginal() {
  275. window.setTimeout = original.setTimeout;
  276. window.setInterval = original.setInterval;
  277. window.requestAnimationFrame = original.requestAnimationFrame;
  278. window.Date = original.originalDate;
  279. modifiedTime = original.dateNow();
  280. startTime = original.dateNow();
  281. }
  282.  
  283. input.addEventListener('change', () => {
  284. if (isEnabled) {
  285. updateSpeed();
  286. }
  287. });
  288.  
  289. toggleButton.addEventListener('click', () => {
  290. isEnabled = !isEnabled;
  291. toggleButton.textContent = isEnabled ? 'Disable' : 'Enable';
  292. toggleButton.style.background = isEnabled ? '#ef4444' : '#3b82f6';
  293.  
  294. if (isEnabled) {
  295. updateSpeed();
  296. } else {
  297. restoreOriginal();
  298. }
  299. });
  300.  
  301. controls.appendChild(input);
  302. controls.appendChild(toggleButton);
  303. document.body.appendChild(controls);
  304. })();