Add up/down navigation buttons to scroll to top or bottom of the page, hide during fullscreen, plus small toggle to make buttons transparent
当前为
// ==UserScript==
// @name scroll up and down buttons
// @namespace http://tampermonkey.net/
// @version 1.3.1
// @description Add up/down navigation buttons to scroll to top or bottom of the page, hide during fullscreen, plus small toggle to make buttons transparent
// @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 });
});
// --- Styles (use classes so toggling is easy) ---
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 { background-color: #333; color: white; font-size: 17px; padding: 8px 12px; border: none; border-radius: 6px; cursor: pointer; box-shadow: 0 2px 6px rgba(0,0,0,0.3); transition: background-color 0.2s, opacity 0.2s; }
.tm-toggle-btn { width: 28px; height: 28px; padding: 0; border-radius: 50%; font-size: 14px; display:flex; align-items:center; justify-content:center; background-color: #555; color: #fff; border:none; cursor:pointer; box-shadow: 0 1px 4px rgba(0,0,0,0.3); }
.tm-scroll-btn.transparent { opacity: 0; }
.tm-scroll-btn.transparent { opacity: 0.1; }
.tm-toggle-btn.active { background-color: #1a73e8; color: #fff; }
`;
document.head.appendChild(style);
// Container
const container = document.createElement('div');
container.className = 'tm-scroll-container';
// Toggle button (small, visible)
const toggleBtn = document.createElement('button');
toggleBtn.className = 'tm-toggle-btn';
toggleBtn.title = 'Toggle button transparency';
toggleBtn.setAttribute('aria-pressed', 'false');
toggleBtn.textContent = '◐'; // icon-like visual
container.appendChild(toggleBtn);
// Up button
const upBtn = document.createElement('button');
upBtn.className = 'tm-scroll-btn';
upBtn.textContent = '▲';
upBtn.title = 'Scroll to top';
upBtn.onclick = () => smoothScrollTo(0);
// Down button
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(upBtn);
container.appendChild(downBtn);
document.body.appendChild(container);
// Persisted state key
const STORAGE_KEY = 'tm_scroll_transparent_v1';
// Apply saved state (if any)
function applyTransparencyState(state) {
if (state) {
upBtn.classList.add('transparent');
downBtn.classList.add('transparent');
toggleBtn.classList.add('active');
toggleBtn.setAttribute('aria-pressed', 'true');
toggleBtn.textContent = '◑';
} else {
upBtn.classList.remove('transparent');
downBtn.classList.remove('transparent');
toggleBtn.classList.remove('active');
toggleBtn.setAttribute('aria-pressed', 'false');
toggleBtn.textContent = '◐';
}
try {
localStorage.setItem(STORAGE_KEY, state ? '1' : '0');
} catch (e) { /* ignore storage errors */ }
}
// Initialize from storage
let saved = null;
try { saved = localStorage.getItem(STORAGE_KEY); } catch (e) { saved = null; }
applyTransparencyState(saved === '1');
// Toggle handler
toggleBtn.addEventListener('click', (e) => {
const enabled = upBtn.classList.contains('transparent');
applyTransparencyState(!enabled);
// keep the small toggle visible (we only change the two main buttons)
e.stopPropagation();
});
// Hide during fullscreen
document.addEventListener('fullscreenchange', () => {
container.style.display = document.fullscreenElement ? 'none' : 'flex';
});
// Ensure container is removed/hidden on pages that replace body later (basic guard)
const observer = new MutationObserver(() => {
if (!document.body || document.body.contains(container) === false) {
if (document.body) document.body.appendChild(container);
}
});
observer.observe(document.documentElement || document, { childList: true, subtree: true });
})();