// ==UserScript== // @name 平滑滚动翻页 // @namespace http://tampermonkey.net/ // @version 2.5 // @description 使用上下键或WS键进行平滑滚动翻页,一次翻页0.9倍页面距离。键盘左右键或A/D键模拟点击页面中【上一页|上一章】【下一页|下一章】按钮,双击只点击【上一章】【下一章】按钮。可以与自动滚屏助手脚本联动。适配开源阅读的web端翻页。 // @author coccvo // @include * // @exclude https://www.bilibili.com/* // @grant none // @icon data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAAXNSR0IArs4c6QAAAVdJREFUWEftlj1Ow0AQhd9YWKKmy3b8FPSpiKUojXMaKOAg0HAZCEUktB0HoMDu4twBkwxaLEvG7C7GzMoowvV437dvfnYIA3/k00+TgiX4Flo5df4BfuVABEx5yxuOSPtSFSQFRvxOq0cjPD9bTXwQ4gBN8frmPghRAJv4dxCCAPRCtH325Zs5OgX4pBkjCNBvKuwmAAMZgFvjSQQ6Z/Cxyx9xB8p4FC+X9NYUnM14Ly7XpQ1CFMAmXou6IMQACJTd69GnCm/fOE3WGcBHQbqAgasHrW58vTBPiksGrncTAEC+0MpZ7ebWaVLkAA6DOGAO9RXhePwUH+yr13aKxIqwPtgG4RI3/4gDVCCUE/hjEDFw0bY9WAr6vAaBHOiOIgjwB55j70IyXU1483U/FHSgst26kjnEg3XBoEtpXX6DruXdeyDYIOqOIF6E3aWryN4APxXqE/8OqY/0IehdmXQAAAAASUVORK5CYII= // @license MIT // @downloadURL none // ==/UserScript== (function () { 'use strict'; // ============================== // 全局状态管理 // ============================== let isScrollKeyPressed = false;// 用户是否正在按 W/S 键滚动 let autoScrollWasActive = false;// 记录自动滚屏是否曾运行(用于恢复) let scrollDistance = window.innerHeight * 0.9; // 每次手动滚动的距离(视口高度的 90%) let clickTimer = null;// 用于实现“单击 vs 双击”逻辑的定时器 const kaiyuanCooldown = 600; // 开源阅读专用冷却时间(毫秒) let lastKaiyuanClick = 0; // ============================== // 页面类型识别 // ============================== function isKaiyuanReaderChapterPage() { try { return window.location.pathname.endsWith('/vue/index.html') && window.location.hash.startsWith('#/chapter'); } catch (e) { return false; } }// 判断当前页面是否为「开源阅读 Web 端」的阅读页 // ============================== // 与「自动滚屏助手」脚本的协同 // ============================== const hasAutoScrollAssistant = () => typeof window.getAutoScrollState === 'function'; // ============================== // 通用页面:通过文本查找并点击导航按钮 // ============================== function clickElementByText(textContent) { const clickableElements = document.querySelectorAll( 'a, button, [role="button"], span[onclick], div[onclick]' ); for (const el of clickableElements) { if (el.textContent.trim() === textContent) { el.click(); return true; } } return false; } // ============================== // 开源阅读 Web 端:通过图标查找章节切换按钮 // ============================== function findChapterButtonByIcon(iconText) { const icons = document.querySelectorAll('.tool-icon .iconfont'); for (const icon of icons) { if (icon.textContent.includes(iconText)) { return icon.parentElement; } } return null; } function triggerToolBarClick() { const toolBar = document.querySelector('.tool-bar'); if (toolBar) toolBar.click(); }//竖屏翻页时关闭菜单 // ============================== // 导航处理:统一翻页入口(A/D/←/→) // ============================== function handleNavigation(direction) { const now = Date.now(); // ── 1. 开源阅读 Web 端:带 cooldown 的图标点击 ── if (isKaiyuanReaderChapterPage()) { if (now - lastKaiyuanClick < kaiyuanCooldown) { return; // 防抖:避免快速连点 } const iconMap = { prev: '', next: '' }; const button = findChapterButtonByIcon(iconMap[direction]); if (button) { button.click(); triggerToolBarClick(); document.body.focus(); lastKaiyuanClick = now; // 更新冷却时间 } return; } // ── 2. 其他网站:纯双击逻辑,无 cooldown 干扰 ── if (clickTimer === null) { // 第一次点击:延迟执行“翻页” clickTimer = setTimeout(() => { const pageTexts = direction === 'prev' ? ['上一页', '<上一页'] : ['下一页', '下一页>']; let clicked = false; for (const text of pageTexts) { if (clickElementByText(text)) { clicked = true; break; } } if (!clicked) { // 降级:尝试“跳章” clickElementByText(direction === 'prev' ? '上一章' : '下一章'); } document.body.focus(); clickTimer = null; }, 300); // 双击判定窗口 } else { // 快速第二次点击:取消翻页,立即跳章 clearTimeout(clickTimer); clickTimer = null; clickElementByText(direction === 'prev' ? '上一章' : '下一章'); document.body.focus(); } } // ============================== // 手动滚动处理(W/S/↑/↓) // ============================== /** * 执行一次视口比例的平滑滚动,并与自动滚屏助手协同。 * @param {boolean} isDown - 是否向下滚动 */ function performManualScroll(isDown) { // 若自动滚屏助手正在运行,先暂停它 if (hasAutoScrollAssistant() && window.getAutoScrollState()) { autoScrollWasActive = true; window.turnOffAutoScroll(); } const distance = isDown ? scrollDistance : -scrollDistance; window.scrollBy({ top: distance, left: 0, behavior: 'smooth' }); } // ============================== // 键盘事件监听 // ============================== function onKeyDown(event) { // 忽略组合键(Ctrl/Alt/Shift + 其他) if (event.ctrlKey || event.altKey || event.shiftKey) return; const target = event.target; // 在输入框、文本域或可编辑区域中不拦截快捷键 if ( target.tagName === 'INPUT' || target.tagName === 'TEXTAREA' || target.isContentEditable || target.closest('[contenteditable="true"]') ) return; const key = event.key.toLowerCase(); let handled = false; // --- 手动滚动:W / S / ↑ / ↓ --- if (['w', 'arrowup'].includes(key)) { event.preventDefault(); performManualScroll(false); isScrollKeyPressed = true; handled = true; } else if (['s', 'arrowdown'].includes(key)) { event.preventDefault(); performManualScroll(true); isScrollKeyPressed = true; handled = true; } // --- 页面/章节导航:A / D / ← / → --- if (key === 'a' || key === 'arrowleft') { event.preventDefault(); handleNavigation('prev'); handled = true; } else if (key === 'd' || key === 'arrowright') { event.preventDefault(); handleNavigation('next'); handled = true; } } function onKeyUp(event) { const key = event.key.toLowerCase(); // 松开滚动键后,延迟恢复自动滚屏(如果之前被暂停) if (['w', 's', 'arrowup', 'arrowdown'].includes(key)) { isScrollKeyPressed = false; if (autoScrollWasActive) { setTimeout(() => { if (!isScrollKeyPressed && hasAutoScrollAssistant()) { window.startAutoScroll?.(); autoScrollWasActive = false; } }, 500); } } } // ============================== // 初始化 // ============================== function init() { scrollDistance = window.innerHeight * 0.9; // 监听键盘事件 document.addEventListener('keydown', onKeyDown, { passive: false }); document.addEventListener('keyup', onKeyUp, { passive: false }); // 响应窗口尺寸变化 window.addEventListener('resize', () => { scrollDistance = window.innerHeight * 0.9; }); } // 等待 DOM 加载完成 if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', init); } else { init(); } })();