您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Add up/down navigation buttons with a settings panel (timer & transparency).
当前为
// ==UserScript== // @name scroll up and down buttons // @namespace http://tampermonkey.net/ // @version 1.5.4 // @description Add up/down navigation buttons with a settings panel (timer & transparency). // @author You // @match *://*/* // @grant none // @license Apache 2.0 // ==/UserScript== (function () { 'use strict'; if (window.top !== window.self) return; if (document.body && document.body.children.length === 1) { const onlyChild = document.body.children[0]; const tag = onlyChild.tagName.toLowerCase(); if (['img', 'video', 'audio', 'embed', 'object'].includes(tag)) return; } let scrollAnimationId = null; function smoothScrollTo(targetY) { if (scrollAnimationId) cancelAnimationFrame(scrollAnimationId); const startY = window.scrollY; const distance = targetY - startY; const duration = 1000; const startTime = performance.now(); function step(currentTime) { const elapsed = currentTime - startTime; const progress = Math.min(elapsed / duration, 1); const ease = progress < 0.5 ? 2 * progress * progress : -1 + (4 - 2 * progress) * progress; window.scrollTo(0, startY + distance * ease); if (progress < 1) { scrollAnimationId = requestAnimationFrame(step); } else { scrollAnimationId = null; } } scrollAnimationId = requestAnimationFrame(step); } function stopScroll() { if (scrollAnimationId) { cancelAnimationFrame(scrollAnimationId); scrollAnimationId = null; } } ['mousedown', 'wheel', 'touchstart', 'keydown'].forEach(evt => { window.addEventListener(evt, stopScroll, { passive: true }); }); const style = document.createElement('style'); style.textContent = ` .tm-scroll-container { position: fixed; bottom: 10px; right: 10px; display: flex; flex-direction: column; gap: 6px; z-index: 9999999; pointer-events: auto; align-items: center; } .tm-scroll-btn { width: 40px; height: 40px; padding: 0; display: flex; align-items: center; justify-content: center; border: none; cursor: pointer; box-shadow: 0 2px 6px rgba(0,0,0,0.3); transition: background-color 0.2s, opacity 0.2s; color: white; font-size: 16px; } .tm-settings-btn { width: 25px; height: 25px; font-size: 13px; padding: 0; display: flex; align-items: center; justify-content: center; border: none; cursor: pointer; box-shadow: 0 2px 6px rgba(0,0,0,0.3); transition: background-color 0.2s, opacity 0.2s; color: white; } .tm-scroll-btn { background-color: #333; border-radius: 6px; } .tm-settings-btn { background-color: #000; border-radius: 50%; font-size: 20px; } .tm-scroll-btn.transparent, .tm-settings-btn.transparent { opacity: 0.1; } .tm-settings-panel { display: none; position: fixed; bottom: 110px; right: 10px; width: 250px; background-color: #3a3a3a; color: #f0f0f0; padding: 15px; border-radius: 8px; box-shadow: 0 4px 12px rgba(0,0,0,0.4); z-index: 10000000; border: 1px solid #555; } .tm-settings-panel hr { border: none; border-top: 1px solid #555; margin: 15px 0; } .tm-settings-panel-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 15px; font-size: 16px; font-weight: bold; } .tm-settings-panel-close { background: none; border: none; color: #f0f0f0; font-size: 24px; cursor: pointer; line-height: 1; padding: 0 5px; } .tm-settings-panel-content .setting { display: flex; flex-direction: column; gap: 8px; } .tm-settings-panel-content .setting-row { display: flex; justify-content: space-between; align-items: center; } .tm-settings-panel-content label { font-size: 14px; } .tm-settings-panel-content input[type="text"] { padding: 8px; border-radius: 4px; border: 1px solid #777; background-color: #2c2c2c; color: white; font-family: monospace; } .tm-settings-panel-content input.error { border-color: #e57373; } .tm-settings-panel-content button { margin-top: 5px; padding: 8px 12px; border-radius: 4px; border: none; cursor: pointer; background-color: #1a73e8; color: white; font-weight: bold; } .tm-settings-panel input::placeholder { color: #E6858A; } .toggle-switch { position: relative; display: inline-block; width: 50px; height: 26px; } .toggle-switch input { opacity: 0; width: 0; height: 0; } .slider { position: absolute; cursor: pointer; top: 0; left: 0; right: 0; bottom: 0; background-color: #666; transition: .3s; border-radius: 26px; } .slider:before { position: absolute; content: ""; height: 18px; width: 18px; left: 4px; bottom: 4px; background-color: white; transition: .3s; border-radius: 50%; } input:checked + .slider { background-color: #1a73e8; } input:checked + .slider:before { transform: translateX(24px); } `; document.head.appendChild(style); const container = document.createElement('div'); container.className = 'tm-scroll-container'; const settingsBtn = document.createElement('button'); settingsBtn.className = 'tm-settings-btn'; settingsBtn.title = 'Open Settings'; settingsBtn.innerHTML = '⚙️'; const upBtn = document.createElement('button'); upBtn.className = 'tm-scroll-btn'; upBtn.textContent = '▲'; upBtn.title = 'Scroll to top'; upBtn.onclick = () => smoothScrollTo(0); const downBtn = document.createElement('button'); downBtn.className = 'tm-scroll-btn'; downBtn.textContent = '▼'; downBtn.title = 'Scroll to bottom'; downBtn.onclick = () => smoothScrollTo(document.documentElement.scrollHeight || document.body.scrollHeight); container.appendChild(settingsBtn); container.appendChild(upBtn); container.appendChild(downBtn); document.body.appendChild(container); const settingsPanel = document.createElement('div'); settingsPanel.className = 'tm-settings-panel'; settingsPanel.innerHTML = ` <div class="tm-settings-panel-header"> <span>Settings</span> <button class="tm-settings-panel-close" title="Close">×</button> </div> <div class="tm-settings-panel-content"> <div class="setting"> <div class="setting-row"> <label for="tm-transparency-toggle">Make buttons transparent</label> <label class="toggle-switch"> <input type="checkbox" id="tm-transparency-toggle"> <span class="slider"></span> </label> </div> </div> <hr> <div class="setting"> <label for="tm-hide-timer-input">Hide buttons temporarily</label> <input type="text" id="tm-hide-timer-input" placeholder="HH-MM-SS" class="tm-hide-timer-input-text-color"> <button id="tm-start-hide-timer">Start Timer</button> </div> </div> `; document.body.appendChild(settingsPanel); const closeSettingsBtn = settingsPanel.querySelector('.tm-settings-panel-close'); const hideTimerInput = document.getElementById('tm-hide-timer-input'); const startHideTimerBtn = document.getElementById('tm-start-hide-timer'); const transparencyToggle = document.getElementById('tm-transparency-toggle'); settingsBtn.addEventListener('click', () => { settingsPanel.style.display = settingsPanel.style.display === 'block' ? 'none' : 'block'; }); closeSettingsBtn.addEventListener('click', () => { settingsPanel.style.display = 'none'; }); startHideTimerBtn.addEventListener('click', () => { const timeValue = hideTimerInput.value; const timePattern = /^(\d+)-(\d{1,2})-(\d{1,2})$/; const match = timeValue.match(timePattern); hideTimerInput.classList.remove('error'); if (!match || parseInt(match[2], 10) >= 60 || parseInt(match[3], 10) >= 60) { hideTimerInput.classList.add('error'); return; } const [ , hours, minutes, seconds] = match.map(Number); const totalMilliseconds = (hours * 3600 + minutes * 60 + seconds) * 1000; if (totalMilliseconds > 0) { container.style.display = 'none'; settingsPanel.style.display = 'none'; hideTimerInput.value = ''; setTimeout(() => { if (!document.fullscreenElement) { container.style.display = 'flex'; } }, totalMilliseconds); } }); const STORAGE_KEY = 'tm_scroll_transparent_v1'; const buttonsToMakeTransparent = [upBtn, downBtn, settingsBtn]; function applyTransparencyState(state) { transparencyToggle.checked = state; buttonsToMakeTransparent.forEach(btn => { state ? btn.classList.add('transparent') : btn.classList.remove('transparent'); }); try { localStorage.setItem(STORAGE_KEY, state ? '1' : '0'); } catch (e) { /* ignore storage errors */ } } transparencyToggle.addEventListener('change', () => { applyTransparencyState(transparencyToggle.checked); }); try { const savedState = localStorage.getItem(STORAGE_KEY); applyTransparencyState(savedState === '1'); } catch (e) { /* ignore storage errors */ } document.addEventListener('fullscreenchange', () => { container.style.display = document.fullscreenElement ? 'none' : 'flex'; }); const observer = new MutationObserver(() => { if (!document.body || document.body.contains(container) === false) { if (document.body) { document.body.appendChild(container); document.body.appendChild(settingsPanel); } } }); observer.observe(document.documentElement || document, { childList: true, subtree: true }); })();
QingJ © 2025
镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址