自动滚动到底部(支持动态加载)

自动滚动到页面底部,持续滚动以触发动态内容加载,若无新内容则在可配置的超时时间后停止。支持多语言,提升使用和检索便捷性。

当前为 2025-04-20 提交的版本,查看 最新版本

  1. // ==UserScript==
  2. // @name Auto Scroll to Bottom with Dynamic Loading
  3. // @name:zh-CN 自动滚动到底部(支持动态加载)
  4. // @name:en Auto Scroll to Bottom with Dynamic Loading
  5. // @name:es Desplazamiento Automático al Final con Carga Dinámica
  6. // @name:ja 自動スクロール(動的読み込み対応)
  7. // @namespace http://tampermonkey.net/
  8. // @version 1.5
  9. // @description Automatically scroll to the bottom of the page, continue scrolling to trigger dynamic content loading, and stop after a configurable timeout if no new content is loaded. Supports multiple languages for better usability and searchability.
  10. // @description:zh-CN 自动滚动到页面底部,持续滚动以触发动态内容加载,若无新内容则在可配置的超时时间后停止。支持多语言,提升使用和检索便捷性。
  11. // @description:en Automatically scroll to the bottom of the page, continue scrolling to trigger dynamic content loading, and stop after a configurable timeout if no new content is loaded. Supports multiple languages for better usability and searchability.
  12. // @description:es Desplaza automáticamente hasta el final de la página, continúa desplazándose para activar la carga de contenido dinámico y se detiene tras un tiempo configurable si no se carga nuevo contenido. Soporta múltiples idiomas para mejor usabilidad y búsqueda.
  13. // @description:ja ページの最下部まで自動でスクロールし、動的コンテンツの読み込みをトリガーするためにスクロールを継続し、新しいコンテンツが読み込まれない場合は設定可能なタイムアウト後に停止します。多言語対応で使いやすさと検索性を向上。
  14. // @author Grok
  15. // @match *://*/*
  16. // @grant GM_setValue
  17. // @grant GM_getValue
  18. // @grant GM_addStyle
  19. // @keyword auto scroll, infinite scroll, dynamic loading, 自动滚动, 无限滚动, 动态加载, desplazamiento automático, carga dinámica, 自動スクロール, 動的読み込み
  20. // ==/UserScript==
  21.  
  22. (function() {
  23. 'use strict';
  24.  
  25. // Language translations
  26. const translations = {
  27. 'zh-CN': {
  28. startScroll: '开始滚动',
  29. stopScroll: '停止滚动',
  30. openConfig: '打开设置',
  31. closeConfig: '关闭',
  32. saveConfig: '保存',
  33. configTitle: '滚动设置',
  34. scrollStep: '滚动步长(像素)',
  35. scrollInterval: '滚动间隔(毫秒)',
  36. scrollBehavior: '滚动行为',
  37. scrollBehaviorSmooth: '平滑',
  38. scrollBehaviorAuto: '即时',
  39. panelPosition: '面板位置',
  40. panelPositionTopRight: '右上',
  41. panelPositionTopLeft: '左上',
  42. panelPositionBottomRight: '右下',
  43. panelPositionBottomLeft: '左下',
  44. maxScrollTimes: '最大滚动次数(0 为无限制)',
  45. timeoutSeconds: '底部超时(秒)',
  46. language: '语言',
  47. languageZhCN: '中文(简体)',
  48. languageEn: 'English',
  49. languageEs: 'Español',
  50. languageJa: '日本語',
  51. saveSuccess: '配置保存成功!',
  52. invalidScrollStep: '滚动步长至少为 1。',
  53. invalidScrollInterval: '滚动间隔至少为 10 毫秒。',
  54. invalidScrollBehavior: '无效的滚动行为。',
  55. invalidPanelPosition: '无效的面板位置。',
  56. invalidMaxScrollTimes: '最大滚动次数必须为 0 或更大。',
  57. invalidTimeoutSeconds: '超时时间至少为 1 秒。'
  58. },
  59. 'en': {
  60. startScroll: 'Start Scroll',
  61. stopScroll: 'Stop Scroll',
  62. openConfig: 'Open Config',
  63. closeConfig: 'Close',
  64. saveConfig: 'Save',
  65. configTitle: 'Scroll Settings',
  66. scrollStep: 'Scroll Step (px)',
  67. scrollInterval: 'Scroll Interval (ms)',
  68. scrollBehavior: 'Scroll Behavior',
  69. scrollBehaviorSmooth: 'Smooth',
  70. scrollBehaviorAuto: 'Auto',
  71. panelPosition: 'Panel Position',
  72. panelPositionTopRight: 'Top Right',
  73. panelPositionTopLeft: 'Top Left',
  74. panelPositionBottomRight: 'Bottom Right',
  75. panelPositionBottomLeft: 'Bottom Left',
  76. maxScrollTimes: 'Max Scroll Times (0 for unlimited)',
  77. timeoutSeconds: 'Timeout at Bottom (seconds)',
  78. language: 'Language',
  79. languageZhCN: 'Chinese (Simplified)',
  80. languageEn: 'English',
  81. languageEs: 'Spanish',
  82. languageJa: 'Japanese',
  83. saveSuccess: 'Configuration saved successfully!',
  84. invalidScrollStep: 'Scroll Step must be at least 1.',
  85. invalidScrollInterval: 'Scroll Interval must be at least 10ms.',
  86. invalidScrollBehavior: 'Invalid Scroll Behavior.',
  87. invalidPanelPosition: 'Invalid Panel Position.',
  88. invalidMaxScrollTimes: 'Max Scroll Times must be 0 or greater.',
  89. invalidTimeoutSeconds: 'Timeout must be at least 1 second.'
  90. },
  91. 'es': {
  92. startScroll: 'Iniciar Desplazamiento',
  93. stopScroll: 'Detener Desplazamiento',
  94. openConfig: 'Abrir Configuración',
  95. closeConfig: 'Cerrar',
  96. saveConfig: 'Guardar',
  97. configTitle: 'Configuración de Desplazamiento',
  98. scrollStep: 'Paso de Desplazamiento (px)',
  99. scrollInterval: 'Intervalo de Desplazamiento (ms)',
  100. scrollBehavior: 'Comportamiento de Desplazamiento',
  101. scrollBehaviorSmooth: 'Suave',
  102. scrollBehaviorAuto: 'Instantáneo',
  103. panelPosition: 'Posición del Panel',
  104. panelPositionTopRight: 'Arriba Derecha',
  105. panelPositionTopLeft: 'Arriba Izquierda',
  106. panelPositionBottomRight: 'Abajo Derecha',
  107. panelPositionBottomLeft: 'Abajo Izquierda',
  108. maxScrollTimes: 'Máximo de Desplazamientos (0 para ilimitado)',
  109. timeoutSeconds: 'Tiempo de Espera en el Fondo (segundos)',
  110. language: 'Idioma',
  111. languageZhCN: 'Chino (Simplificado)',
  112. languageEn: 'Inglés',
  113. languageEs: 'Español',
  114. languageJa: 'Japonés',
  115. saveSuccess: '¡Configuración guardada con éxito!',
  116. invalidScrollStep: 'El paso de desplazamiento debe ser al menos 1.',
  117. invalidScrollInterval: 'El intervalo de desplazamiento debe ser al menos 10 ms.',
  118. invalidScrollBehavior: 'Comportamiento de desplazamiento inválido.',
  119. invalidPanelPosition: 'Posición del panel inválida.',
  120. invalidMaxScrollTimes: 'El máximo de desplazamientos debe ser 0 o mayor.',
  121. invalidTimeoutSeconds: 'El tiempo de espera debe ser al menos 1 segundo.'
  122. },
  123. 'ja': {
  124. startScroll: 'スクロール開始',
  125. stopScroll: 'スクロール停止',
  126. openConfig: '設定を開く',
  127. closeConfig: '閉じる',
  128. saveConfig: '保存',
  129. configTitle: 'スクロール設定',
  130. scrollStep: 'スクロールステップ(ピクセル)',
  131. scrollInterval: 'スクロール間隔(ミリ秒)',
  132. scrollBehavior: 'スクロール動作',
  133. scrollBehaviorSmooth: 'スムーズ',
  134. scrollBehaviorAuto: '即時',
  135. panelPosition: 'パネル位置',
  136. panelPositionTopRight: '右上',
  137. panelPositionTopLeft: '左上',
  138. panelPositionBottomRight: '右下',
  139. panelPositionBottomLeft: '左下',
  140. maxScrollTimes: '最大スクロール回数(0で無制限)',
  141. timeoutSeconds: '底部でのタイムアウト(秒)',
  142. language: '言語',
  143. languageZhCN: '中国語(簡体)',
  144. languageEn: '英語',
  145. languageEs: 'スペイン語',
  146. languageJa: '日本語',
  147. saveSuccess: '設定が正常に保存されました!',
  148. invalidScrollStep: 'スクロールステップは1以上でなければなりません。',
  149. invalidScrollInterval: 'スクロール間隔は10ミリ秒以上でなければなりません。',
  150. invalidScrollBehavior: '無効なスクロール動作です。',
  151. invalidPanelPosition: '無効なパネル位置です。',
  152. invalidMaxScrollTimes: '最大スクロール回数は0以上でなければなりません。',
  153. invalidTimeoutSeconds: 'タイムアウトは1秒以上でなければなりません。'
  154. }
  155. };
  156.  
  157. // Default configuration
  158. const defaultConfig = {
  159. scrollStep: 100, // Pixels per scroll
  160. scrollInterval: 50, // Interval between scrolls (ms)
  161. scrollBehavior: 'smooth', // Scroll behavior: smooth or auto
  162. panelPosition: 'top-right', // Panel position
  163. maxScrollTimes: 0, // Max scroll actions, 0 for unlimited
  164. timeoutSeconds: 10, // Timeout in seconds before stopping at bottom
  165. language: 'zh-CN' // Default language
  166. };
  167.  
  168. // Load saved configuration or use defaults
  169. let config = {
  170. scrollStep: GM_getValue('scrollStep', defaultConfig.scrollStep),
  171. scrollInterval: GM_getValue('scrollInterval', defaultConfig.scrollInterval),
  172. scrollBehavior: GM_getValue('scrollBehavior', defaultConfig.scrollBehavior),
  173. panelPosition: GM_getValue('panelPosition', defaultConfig.panelPosition),
  174. maxScrollTimes: GM_getValue('maxScrollTimes', defaultConfig.maxScrollTimes),
  175. timeoutSeconds: GM_getValue('timeoutSeconds', defaultConfig.timeoutSeconds),
  176. language: GM_getValue('language', defaultConfig.language)
  177. };
  178.  
  179. let isScrolling = false;
  180. let scrollTimer = null;
  181. let scrollCount = 0;
  182. let lastScrollTime = 0;
  183. let atBottomSince = null;
  184. let lastScrollHeight = 0;
  185.  
  186. // Inject styles
  187. GM_addStyle(`
  188. #scrollControlPanel {
  189. position: fixed;
  190. ${getPanelPositionStyles(config.panelPosition)}
  191. background: #fff;
  192. border: 1px solid #ccc;
  193. padding: 10px;
  194. z-index: 10000;
  195. box-shadow: 0 2px 5px rgba(0,0,0,0.2);
  196. border-radius: 5px;
  197. }
  198. #scrollConfigPanel {
  199. position: fixed;
  200. top: 50%;
  201. left: 50%;
  202. transform: translate(-50%, -50%);
  203. background: #fff;
  204. border: 1px solid #ccc;
  205. padding: 20px;
  206. z-index: 10001;
  207. box-shadow: 0 4px 10px rgba(0,0,0,0.3);
  208. border-radius: 5px;
  209. display: none;
  210. width: 300px;
  211. color: #333;
  212. font-family: Arial, sans-serif;
  213. }
  214. #scrollControlPanel button, #scrollConfigPanel button {
  215. margin: 5px;
  216. padding: 5px 10px;
  217. cursor: pointer;
  218. background: #007bff;
  219. color: #fff;
  220. border: none;
  221. border-radius: 3px;
  222. font-size: 14px;
  223. }
  224. #scrollControlPanel button:hover, #scrollConfigPanel button:hover {
  225. background: #0056b3;
  226. }
  227. #scrollControlPanel .toggle-btn {
  228. background: #28a745;
  229. }
  230. #scrollControlPanel .toggle-btn.stop {
  231. background: #dc3545;
  232. }
  233. #scrollConfigPanel .close-btn {
  234. background: #6c757d;
  235. }
  236. #scrollConfigPanel label {
  237. display: block;
  238. margin: 10px 0;
  239. font-size: 14px;
  240. color: #333;
  241. }
  242. #scrollConfigPanel input, #scrollConfigPanel select {
  243. width: 100px;
  244. padding: 3px;
  245. margin-left: 5px;
  246. border: 1px solid #ccc;
  247. border-radius: 3px;
  248. color: #333;
  249. background: #f9f9f9;
  250. }
  251. #scrollConfigPanel input:hover, #scrollConfigPanel select:hover {
  252. background: #e0e0e0;
  253. }
  254. #scrollConfigPanel h3 {
  255. margin: 0 0 10px;
  256. font-size: 16px;
  257. color: #333;
  258. }
  259. `);
  260.  
  261. // Get styles for panel position
  262. function getPanelPositionStyles(position) {
  263. switch (position) {
  264. case 'top-left':
  265. return 'top: 20px; left: 20px;';
  266. case 'top-right':
  267. return 'top: 20px; right: 20px;';
  268. case 'bottom-left':
  269. return 'bottom: 20px; left: 20px;';
  270. case 'bottom-right':
  271. return 'bottom: 20px; right: 20px;';
  272. default:
  273. return 'top: 20px; right: 20px;';
  274. }
  275. }
  276.  
  277. // Get translation for current language
  278. function t(key) {
  279. return translations[config.language][key] || translations['en'][key];
  280. }
  281.  
  282. // Create control panel
  283. function createControlPanel() {
  284. const panel = document.createElement('div');
  285. panel.id = 'scrollControlPanel';
  286. panel.innerHTML = `
  287. <button id="toggleScrollBtn" class="toggle-btn">${t('startScroll')}</button>
  288. <button id="openConfigBtn">${t('openConfig')}</button>
  289. `;
  290. document.body.appendChild(panel);
  291.  
  292. // Bind events
  293. document.getElementById('toggleScrollBtn').addEventListener('click', toggleScroll);
  294. document.getElementById('openConfigBtn').addEventListener('click', openConfigPanel);
  295. }
  296.  
  297. // Create configuration panel
  298. function createConfigPanel() {
  299. const configPanel = document.createElement('div');
  300. configPanel.id = 'scrollConfigPanel';
  301. configPanel.innerHTML = `
  302. <h3>${t('configTitle')}</h3>
  303. <label>${t('scrollStep')}: <input type="number" id="scrollStepInput" value="${config.scrollStep}" min="1"></label>
  304. <label>${t('scrollInterval')}: <input type="number" id="scrollIntervalInput" value="${config.scrollInterval}" min="10"></label>
  305. <label>${t('scrollBehavior')}:
  306. <select id="scrollBehaviorSelect">
  307. <option value="smooth" ${config.scrollBehavior === 'smooth' ? 'selected' : ''}>${t('scrollBehaviorSmooth')}</option>
  308. <option value="auto" ${config.scrollBehavior === 'auto' ? 'selected' : ''}>${t('scrollBehaviorAuto')}</option>
  309. </select>
  310. </label>
  311. <label>${t('panelPosition')}:
  312. <select id="panelPositionSelect">
  313. <option value="top-right" ${config.panelPosition === 'top-right' ? 'selected' : ''}>${t('panelPositionTopRight')}</option>
  314. <option value="top-left" ${config.panelPosition === 'top-left' ? 'selected' : ''}>${t('panelPositionTopLeft')}</option>
  315. <option value="bottom-right" ${config.panelPosition === 'bottom-right' ? 'selected' : ''}>${t('panelPositionBottomRight')}</option>
  316. <option value="bottom-left" ${config.panelPosition === 'bottom-left' ? 'selected' : ''}>${t('panelPositionBottomLeft')}</option>
  317. </select>
  318. </label>
  319. <label>${t('maxScrollTimes')}: <input type="number" id="maxScrollTimesInput" value="${config.maxScrollTimes}" min="0"></label>
  320. <label>${t('timeoutSeconds')}: <input type="number" id="timeoutSecondsInput" value="${config.timeoutSeconds}" min="1"></label>
  321. <label>${t('language')}:
  322. <select id="languageSelect">
  323. <option value="zh-CN" ${config.language === 'zh-CN' ? 'selected' : ''}>${t('languageZhCN')}</option>
  324. <option value="en" ${config.language === 'en' ? 'selected' : ''}>${t('languageEn')}</option>
  325. <option value="es" ${config.language === 'es' ? 'selected' : ''}>${t('languageEs')}</option>
  326. <option value="ja" ${config.language === 'ja' ? 'selected' : ''}>${t('languageJa')}</option>
  327. </select>
  328. </label>
  329. <button id="saveConfigBtn">${t('saveConfig')}</button>
  330. <button id="closeConfigBtn" class="close-btn">${t('closeConfig')}</button>
  331. `;
  332. document.body.appendChild(configPanel);
  333.  
  334. // Bind events
  335. document.getElementById('saveConfigBtn').addEventListener('click', saveConfig);
  336. document.getElementById('closeConfigBtn').addEventListener('click', closeConfigPanel);
  337. }
  338.  
  339. // Open configuration panel
  340. function openConfigPanel() {
  341. const configPanel = document.getElementById('scrollConfigPanel');
  342. configPanel.style.display = 'block';
  343. }
  344.  
  345. // Close configuration panel
  346. function closeConfigPanel() {
  347. const configPanel = document.getElementById('scrollConfigPanel');
  348. configPanel.style.display = 'none';
  349. }
  350.  
  351. // Toggle scrolling
  352. function toggleScroll() {
  353. const toggleBtn = document.getElementById('toggleScrollBtn');
  354. if (isScrolling) {
  355. clearInterval(scrollTimer);
  356. isScrolling = false;
  357. atBottomSince = null;
  358. toggleBtn.textContent = t('startScroll');
  359. toggleBtn.classList.remove('stop');
  360. } else {
  361. scrollCount = 0;
  362. lastScrollHeight = document.documentElement.scrollHeight;
  363. atBottomSince = null;
  364. startScroll();
  365. isScrolling = true;
  366. toggleBtn.textContent = t('stopScroll');
  367. toggleBtn.classList.add('stop');
  368. }
  369. }
  370.  
  371. // Start scrolling
  372. function startScroll() {
  373. if (scrollTimer) clearInterval(scrollTimer);
  374.  
  375. scrollTimer = setInterval(() => {
  376. const now = Date.now();
  377. if (now - lastScrollTime < config.scrollInterval) return;
  378.  
  379. lastScrollTime = now;
  380. window.scrollBy({ top: config.scrollStep, behavior: config.scrollBehavior });
  381. scrollCount++;
  382.  
  383. // Check if max scroll times reached
  384. if (config.maxScrollTimes > 0 && scrollCount >= config.maxScrollTimes) {
  385. clearInterval(scrollTimer);
  386. isScrolling = false;
  387. atBottomSince = null;
  388. document.getElementById('toggleScrollBtn').textContent = t('startScroll');
  389. document.getElementById('toggleScrollBtn').classList.remove('stop');
  390. console.log('Reached max scroll times');
  391. return;
  392. }
  393.  
  394. // Check if at page bottom
  395. const isAtBottom = window.innerHeight + Math.ceil(window.scrollY) >= document.documentElement.scrollHeight;
  396. if (isAtBottom) {
  397. const currentScrollHeight = document.documentElement.scrollHeight;
  398. if (currentScrollHeight > lastScrollHeight) {
  399. // New content loaded, reset timeout
  400. lastScrollHeight = currentScrollHeight;
  401. atBottomSince = null;
  402. console.log('New content loaded, continuing scroll');
  403. } else {
  404. // No new content, start or continue timeout
  405. if (!atBottomSince) {
  406. atBottomSince = now;
  407. }
  408. if (now - atBottomSince >= config.timeoutSeconds * 1000) {
  409. // Timeout reached, stop scrolling
  410. clearInterval(scrollTimer);
  411. isScrolling = false;
  412. atBottomSince = null;
  413. document.getElementById('toggleScrollBtn').textContent = t('startScroll');
  414. document.getElementById('toggleScrollBtn').classList.remove('stop');
  415. console.log('Timeout reached, no new content, stopping scroll');
  416. return;
  417. }
  418. // Continue trying to scroll to trigger loading
  419. console.log('At bottom, attempting to trigger more content');
  420. }
  421. } else {
  422. // Not at bottom, reset timeout
  423. atBottomSince = null;
  424. }
  425. }, config.scrollInterval / 2);
  426. }
  427.  
  428. // Detect manual scrolling to pause auto-scroll
  429. let manualScrollTimeout = null;
  430. window.addEventListener('scroll', () => {
  431. if (isScrolling && Date.now() - lastScrollTime > config.scrollInterval * 2) {
  432. clearInterval(scrollTimer);
  433. clearTimeout(manualScrollTimeout);
  434. manualScrollTimeout = setTimeout(() => {
  435. if (isScrolling) startScroll();
  436. }, 1000);
  437. }
  438. });
  439.  
  440. // Save configuration
  441. function saveConfig() {
  442. const scrollStep = parseInt(document.getElementById('scrollStepInput').value);
  443. const scrollInterval = parseInt(document.getElementById('scrollIntervalInput').value);
  444. const scrollBehavior = document.getElementById('scrollBehaviorSelect').value;
  445. const panelPosition = document.getElementById('panelPositionSelect').value;
  446. const maxScrollTimes = parseInt(document.getElementById('maxScrollTimesInput').value);
  447. const timeoutSeconds = parseInt(document.getElementById('timeoutSecondsInput').value);
  448. const language = document.getElementById('languageSelect').value;
  449.  
  450. // Validate inputs
  451. if (isNaN(scrollStep) || scrollStep < 1) {
  452. alert(t('invalidScrollStep'));
  453. return;
  454. }
  455. if (isNaN(scrollInterval) || scrollInterval < 10) {
  456. alert(t('invalidScrollInterval'));
  457. return;
  458. }
  459. if (!['smooth', 'auto'].includes(scrollBehavior)) {
  460. alert(t('invalidScrollBehavior'));
  461. return;
  462. }
  463. if (!['top-right', 'top-left', 'bottom-right', 'bottom-left'].includes(panelPosition)) {
  464. alert(t('invalidPanelPosition'));
  465. return;
  466. }
  467. if (isNaN(maxScrollTimes) || maxScrollTimes < 0) {
  468. alert(t('invalidMaxScrollTimes'));
  469. return;
  470. }
  471. if (isNaN(timeoutSeconds) || timeoutSeconds < 1) {
  472. alert(t('invalidTimeoutSeconds'));
  473. return;
  474. }
  475. if (!['zh-CN', 'en', 'es', 'ja'].includes(language)) {
  476. alert('Invalid language.');
  477. return;
  478. }
  479.  
  480. // Update config
  481. config.scrollStep = scrollStep;
  482. config.scrollInterval = scrollInterval;
  483. config.scrollBehavior = scrollBehavior;
  484. config.panelPosition = panelPosition;
  485. config.maxScrollTimes = maxScrollTimes;
  486. config.timeoutSeconds = timeoutSeconds;
  487. config.language = language;
  488.  
  489. // Save to storage
  490. GM_setValue('scrollStep', config.scrollStep);
  491. GM_setValue('scrollInterval', config.scrollInterval);
  492. GM_setValue('scrollBehavior', config.scrollBehavior);
  493. GM_setValue('panelPosition', config.panelPosition);
  494. GM_setValue('maxScrollTimes', config.maxScrollTimes);
  495. GM_setValue('timeoutSeconds', config.timeoutSeconds);
  496. GM_setValue('language', config.language);
  497.  
  498. // Update panel position
  499. const controlPanel = document.getElementById('scrollControlPanel');
  500. controlPanel.style.cssText = `
  501. position: fixed;
  502. ${getPanelPositionStyles(config.panelPosition)}
  503. background: #fff;
  504. border: 1px solid #ccc;
  505. padding: 10px;
  506. z-index: 10000;
  507. box-shadow: 0 2px 5px rgba(0,0,0,0.2);
  508. border-radius: 5px;
  509. `;
  510.  
  511. // Close config panel
  512. closeConfigPanel();
  513.  
  514. // Recreate panels to update language
  515. document.getElementById('scrollControlPanel').remove();
  516. document.getElementById('scrollConfigPanel').remove();
  517. createControlPanel();
  518. createConfigPanel();
  519.  
  520. // Notify user
  521. alert(t('saveSuccess'));
  522.  
  523. // Restart scrolling if active
  524. if (isScrolling) {
  525. clearInterval(scrollTimer);
  526. scrollCount = 0;
  527. lastScrollHeight = document.documentElement.scrollHeight;
  528. atBottomSince = null;
  529. startScroll();
  530. }
  531. }
  532.  
  533. // Initialize
  534. createControlPanel();
  535. createConfigPanel();
  536. })();

QingJ © 2025

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