// ==UserScript== // @name 超星学习通自动阅读 // @namespace http://tampermonkey.net/ // @version 1.1.0 // @description 支持后台运行、最小化、无限时长、记忆配置(反馈群:612441267) // @author lakay666 // @match https://readsvr-fanya.sslibrary.com/n/readsvr/book/mooc/* // @run-at document-end // @grant none // ==/UserScript== (function() { 'use strict'; // ==================== 加载保存的设置 ==================== const STORAGE_KEY = 'mooc_scroll_config'; function loadConfig() { const saved = localStorage.getItem(STORAGE_KEY); if (saved) { try { return JSON.parse(saved); } catch(e) {} } return { speed: 100, // 滚动速度 px/s minutes: 10, // 时长(分钟),0=无限 pauseBottom: 3, // 到底暂停秒 pauseTop: 2 // 到顶暂停秒 }; } function saveConfig(config) { localStorage.setItem(STORAGE_KEY, JSON.stringify(config)); } let config = loadConfig(); // ========================================================== console.log('[自动滚动] 脚本已加载,配置:', config); function createPanel(container) { if (document.getElementById('scroll-panel')) return; const div = document.createElement('div'); div.id = 'scroll-panel'; div.innerHTML = `
📜 循环滚动 0次
就绪
后台运行 ✅ | 记忆配置 ✅ | Alt+Q/W/E
`; document.body.appendChild(div); // 面板事件 const settingsArea = document.getElementById('settings-area'); document.getElementById('btn-settings-toggle').addEventListener('click', () => { settingsArea.style.display = settingsArea.style.display === 'none' ? 'block' : 'none'; }); document.getElementById('input-speed').addEventListener('input', function() { config.speed = parseInt(this.value); document.getElementById('speed-label').textContent = config.speed; saveConfig(config); }); document.getElementById('input-time').addEventListener('change', function() { config.minutes = Math.max(0, parseInt(this.value) || 0); this.value = config.minutes; saveConfig(config); }); document.getElementById('input-bottom').addEventListener('input', function() { config.pauseBottom = parseFloat(this.value); document.getElementById('bottom-label').textContent = config.pauseBottom; saveConfig(config); }); document.getElementById('input-top').addEventListener('input', function() { config.pauseTop = parseFloat(this.value); document.getElementById('top-label').textContent = config.pauseTop; saveConfig(config); }); return {}; } function waitAndStart() { const checkInterval = setInterval(() => { let container = document.querySelector('#Readweb'); if (!container) { const all = [...document.querySelectorAll('*')]; let best = null, maxH = 0; all.forEach(el => { const style = getComputedStyle(el); if (style.overflow === 'auto' || style.overflow === 'scroll' || style.overflowY === 'auto' || style.overflowY === 'scroll') { const h = el.scrollHeight - el.clientHeight; if (h > maxH && h > 100) { maxH = h; best = el; } } }); container = best; } if (container) { clearInterval(checkInterval); console.log('[自动滚动] 容器:', container.id || container.className || container.tagName); console.log('[自动滚动] scrollHeight:', container.scrollHeight, 'clientHeight:', container.clientHeight); createPanel(container); startScrolling(container); } }, 500); } function startScrolling(container) { const statusEl = document.getElementById('scroll-status'); const loopCountEl = document.getElementById('loop-count'); let isRunning = false; let isPaused = false; let elapsedSeconds = 0; let direction = 'down'; let scrollAccumulator = 0; let intervalId = null; let pauseTimer = null; let loopCount = 0; const TICK_MS = 50; // 每50ms更新一次(后台也能跑) function updateStatus(text) { if (statusEl) statusEl.textContent = text; } function updateLoopCount() { if (loopCountEl) loopCountEl.textContent = loopCount + '次'; } function getMaxScroll() { return container.scrollHeight - container.clientHeight; } function tick() { if (!isRunning || isPaused) return; elapsedSeconds += TICK_MS / 1000; const totalSeconds = config.minutes > 0 ? config.minutes * 60 : Infinity; if (elapsedSeconds >= totalSeconds) { stop(); updateStatus(`✅ 已完成 | ${loopCount}次循环`); return; } const deltaPx = config.speed * (TICK_MS / 1000); scrollAccumulator += deltaPx; if (scrollAccumulator >= 10) { const amount = scrollAccumulator; scrollAccumulator = 0; const current = container.scrollTop; const maxScroll = getMaxScroll(); if (direction === 'down') { const newTop = current + amount; if (newTop >= maxScroll - 1) { container.scrollTop = maxScroll; loopCount++; updateLoopCount(); updateStatus(`📄 到底 | 🔄${loopCount}次 | 暂停中...`); if (pauseTimer) clearTimeout(pauseTimer); pauseTimer = setTimeout(() => { direction = 'up'; scrollAccumulator = 0; }, config.pauseBottom * 1000); return; } container.scrollTop = newTop; } else { const newTop = current - amount; if (newTop <= 1) { container.scrollTop = 0; updateStatus(`🔝 到顶 | 🔄${loopCount}次 | 暂停中...`); if (pauseTimer) clearTimeout(pauseTimer); pauseTimer = setTimeout(() => { direction = 'down'; scrollAccumulator = 0; }, config.pauseTop * 1000); return; } container.scrollTop = newTop; } const remaining = totalSeconds === Infinity ? '∞' : Math.round(totalSeconds - elapsedSeconds); const pct = totalSeconds === Infinity ? '∞' : ((elapsedSeconds / totalSeconds) * 100).toFixed(1); const max = Math.round(getMaxScroll()); const cur = Math.round(container.scrollTop); updateStatus(`${pct}% | ⏱${remaining}s | ${direction==='down'?'⬇':'⬆'} ${cur}/${max}`); } } function start() { if (isRunning && !isPaused) return; if (isPaused) { isPaused = false; updateStatus('▶ 继续滚动'); return; } isRunning = true; isPaused = false; elapsedSeconds = 0; direction = 'down'; scrollAccumulator = 0; loopCount = 0; updateLoopCount(); if (intervalId) clearInterval(intervalId); intervalId = setInterval(tick, TICK_MS); updateStatus('🚀 滚动中...'); console.log('[自动滚动] 速度:', config.speed, 'px/s | 时长:', config.minutes > 0 ? config.minutes+'min' : '无限'); } function pause() { isPaused = true; updateStatus('⏸ 已暂停'); } function stop() { isRunning = false; isPaused = false; if (intervalId) clearInterval(intervalId); if (pauseTimer) clearTimeout(pauseTimer); updateStatus('⏹ 已停止'); console.log('[自动滚动] 停止 | 运行:', Math.round(elapsedSeconds), '秒 | 循环:', loopCount, '次'); } document.getElementById('btn-start').addEventListener('click', start); document.getElementById('btn-pause').addEventListener('click', pause); document.getElementById('btn-stop').addEventListener('click', stop); document.addEventListener('keydown', (e) => { if (e.altKey && e.key === 'q') { e.preventDefault(); start(); } if (e.altKey && e.key === 'w') { e.preventDefault(); pause(); } if (e.altKey && e.key === 'e') { e.preventDefault(); stop(); } }); setTimeout(start, 2000); } waitAndStart(); })();