您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
[en] A modern scroll progress bar at the bottom of the screen and smart scroll-to-top/bottom buttons (SVG, discreet, SPA & mobile friendly).
当前为
// ==UserScript== // @name Progress Bar and Quick Up and Down Buttons // @name:pt-BR BF - Barra de progressão e Botões de Subida e Decida Rápido // @namespace https://github.com/BrunoFortunatto // @version 1.0 // @description [en] A modern scroll progress bar at the bottom of the screen and smart scroll-to-top/bottom buttons (SVG, discreet, SPA & mobile friendly). // @description:pt-BR Adiciona uma barra de progresso de rolagem moderna na parte inferior da tela e botões de subir/descer inteligentes (SVG, discretos, compatíveis com SPA e mobile). // @author Bruno Fortunato // @match *://*/* // @grant none // @license MIT // ==/UserScript== (function() { 'use strict'; // VERIFICAÇÃO PARA EVITAR IFRAMES if (window.self !== window.top) { // Se este script estiver rodando dentro de um iframe, ele para aqui. return; } const INACTIVITY_TIMEOUT = 2000; // Tempo em milissegundos (2 segundos) para esconder os botões const RIGHT_EDGE_THRESHOLD_PX = 100; // Distância da borda direita para ativar os botões no PC let inactivityTimer; let buttonContainer; let progressBar; // Nova variável para a barra de progresso // --- Funções Auxiliares para Controle dos Botões --- // Função para esconder os botões function hideButtons() { if (buttonContainer) { buttonContainer.style.opacity = '0'; buttonContainer.style.pointerEvents = 'none'; // Desabilita cliques quando invisível } } // Função para mostrar os botões e resetar o timer function showButtonsAndResetTimer() { // Verifica se a página é rolavel o suficiente antes de mostrar const scrolledEnough = document.body.scrollTop > 20 || document.documentElement.scrollTop > 20; const pageIsScrollable = document.body.scrollHeight > window.innerHeight; if (scrolledEnough && pageIsScrollable) { if (buttonContainer) { buttonContainer.style.opacity = '1'; buttonContainer.style.pointerEvents = 'auto'; // Habilita cliques clearTimeout(inactivityTimer); inactivityTimer = setTimeout(hideButtons, INACTIVITY_TIMEOUT); } } else { // Se não for rolavel ou estiver no topo, garante que estejam escondidos hideButtons(); clearTimeout(inactivityTimer); } } // --- Funções para a Barra de Progresso --- function updateProgressBar() { const docElem = document.documentElement; const body = document.body; const scrollTop = docElem.scrollTop || body.scrollTop; // Posição de rolagem atual const scrollHeight = docElem.scrollHeight || body.scrollHeight; // Altura total do conteúdo const clientHeight = docElem.clientHeight || window.innerHeight; // Altura visível da janela const totalScrollableHeight = scrollHeight - clientHeight; // Altura total que pode ser rolada let scrollProgress = 0; if (totalScrollableHeight > 0) { // Apenas se a página for rolavel scrollProgress = (scrollTop / totalScrollableHeight) * 100; progressBar.style.width = scrollProgress + '%'; progressBar.style.display = 'block'; // Mostra a barra } else { progressBar.style.width = '0%'; // Reseta a largura para 0 progressBar.style.display = 'none'; // Esconde se a página não for rolavel } } // --- Inicialização dos Elementos (Botões e Barra de Progresso) --- function initializeScrollElements() { // --- Inicialização dos Botões --- // Se o container de botões já existe, remove para recriar (útil para SPAs) if (buttonContainer && buttonContainer.parentNode) { buttonContainer.parentNode.removeChild(buttonContainer); } // Cria o container para os botões para centralizá-los buttonContainer = document.createElement('div'); buttonContainer.style.position = 'fixed'; buttonContainer.style.right = '20px'; // Distância da margem direita buttonContainer.style.top = '50%'; // Começa no meio vertical buttonContainer.style.transform = 'translateY(-50%)'; // Ajusta para centralizar exatamente buttonContainer.style.zIndex = '9999'; buttonContainer.style.display = 'flex'; buttonContainer.style.flexDirection = 'column'; // Organiza os botões em coluna buttonContainer.style.gap = '10px'; // Espaço entre os botões buttonContainer.style.opacity = '0'; // Começa invisível buttonContainer.style.transition = 'opacity 0.3s ease-in-out'; // Transição suave para aparecer/desaparecer buttonContainer.style.pointerEvents = 'none'; // Desabilita cliques quando invisível document.body.appendChild(buttonContainer); // Estilo base para os botões const baseButtonStyle = { backgroundColor: 'rgba(0, 123, 255, 0.5)', // Azul com 50% de opacidade color: 'white', // Cor da seta (herda para o SVG) border: 'none', borderRadius: '50%', // Torna o botão circular width: '50px', // Largura para formar o círculo height: '50px', // Altura para formar o círculo display: 'flex', justifyContent: 'center', alignItems: 'center', cursor: 'pointer', boxShadow: '0 3px 6px rgba(0,0,0,0.3)', // Sombra mais proeminente transition: 'background-color 0.2s ease, transform 0.2s ease', // Transição suave para hover e click }; // Estilo para hover const hoverStyle = { backgroundColor: 'rgba(0, 123, 255, 0.9)', // Mais opaco ao passar o mouse transform: 'scale(1.05)', // Aumenta levemente ao passar o mouse }; // --- SVGs das setas --- const topArrowSVG = ` <svg xmlns="http://www.w3.org/2000/svg" width="100%" height="100%" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"> <polyline points="12 19 12 5"></polyline> <polyline points="5 12 12 5 19 12"></polyline> </svg> `; const bottomArrowSVG = ` <svg xmlns="http://www.w3.org/2000/svg" width="100%" height="100%" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"> <polyline points="12 5 12 19"></polyline> <polyline points="5 12 12 19 19 12"></polyline> </svg> `; // Cria o botão "Subir" const topButton = document.createElement('button'); Object.assign(topButton.style, baseButtonStyle); topButton.innerHTML = topArrowSVG; // Adiciona o SVG ao botão topButton.onmouseover = () => Object.assign(topButton.style, hoverStyle); topButton.onmouseout = () => Object.assign(topButton.style, baseButtonStyle); topButton.onclick = () => { window.scrollTo({ top: 0, behavior: 'smooth' }); showButtonsAndResetTimer(); // Resetar o timer após o clique }; buttonContainer.appendChild(topButton); // Cria o botão "Descer" const bottomButton = document.createElement('button'); Object.assign(bottomButton.style, baseButtonStyle); bottomButton.innerHTML = bottomArrowSVG; // Adiciona o SVG ao botão bottomButton.onmouseover = () => Object.assign(bottomButton.style, hoverStyle); bottomButton.onmouseout = () => Object.assign(bottomButton.style, baseButtonStyle); bottomButton.onclick = () => { window.scrollTo({ top: document.body.scrollHeight, behavior: 'smooth' }); showButtonsAndResetTimer(); // Resetar o timer após o clique }; buttonContainer.appendChild(bottomButton); // --- Inicialização da Barra de Progresso --- // Se a barra de progresso já existe, remove para recriar if (progressBar && progressBar.parentNode) { progressBar.parentNode.removeChild(progressBar); } progressBar = document.createElement('div'); progressBar.style.position = 'fixed'; progressBar.style.bottom = '0'; progressBar.style.left = '0'; progressBar.style.width = '0%'; // Começa com 0% de largura progressBar.style.height = '5px'; // Altura da barra progressBar.style.zIndex = '10000'; // Garante que fique acima de outros elementos progressBar.style.background = 'linear-gradient(to right, #007bff, #00c7ff, #007bff)'; // Gradiente azul com "luzes" progressBar.style.boxShadow = '0 -2px 10px rgba(0, 123, 255, 0.7)'; // Sombra com efeito de luz progressBar.style.transition = 'width 0.2s ease-out'; // Transição suave para o progresso progressBar.style.display = 'none'; // Inicialmente oculta document.body.appendChild(progressBar); // --- Eventos para mostrar/esconder os botões e atualizar a barra de progresso --- // Eventos de rolagem (funciona para desktop e mobile) window.onscroll = () => { showButtonsAndResetTimer(); // Lógica dos botões updateProgressBar(); // Lógica da barra de progresso }; // Eventos de mouse para desktop: Só ativa os botões se o mouse estiver perto da borda direita document.onmousemove = (event) => { if (event.clientX > (window.innerWidth - RIGHT_EDGE_THRESHOLD_PX)) { showButtonsAndResetTimer(); } }; // Eventos de toque para mobile (usando addEventListener para 'passive') document.addEventListener('touchstart', showButtonsAndResetTimer, { passive: true }); document.addEventListener('touchmove', showButtonsAndResetTimer, { passive: true }); // --- Observador de Mutação para SPAs (detecta mudanças no DOM) --- const observer = new MutationObserver(mutations => { mutations.forEach(mutation => { if (mutation.type === 'childList' || mutation.type === 'subtree') { showButtonsAndResetTimer(); // Para botões updateProgressBar(); // Para barra de progresso } }); }); observer.observe(document.body, { childList: true, subtree: true, attributes: false, characterData: false }); // --- Intercepta a API de Histórico para SPAs (detecta mudanças de URL sem reload) --- const originalPushState = history.pushState; const originalReplaceState = history.replaceState; history.pushState = function() { originalPushState.apply(this, arguments); showButtonsAndResetTimer(); // Para botões updateProgressBar(); // Para barra de progresso }; history.replaceState = function() { originalReplaceState.apply(this, arguments); showButtonsAndResetTimer(); // Para botões updateProgressBar(); // Para barra de progresso }; // Garante que os elementos apareçam/desapareçam/atualizem corretamente na carga inicial window.addEventListener('load', () => { showButtonsAndResetTimer(); updateProgressBar(); }); window.addEventListener('DOMContentLoaded', () => { showButtonsAndResetTimer(); updateProgressBar(); }); } // Inicializa todos os elementos quando o script é carregado initializeScrollElements(); })();
QingJ © 2025
镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址