您需要先安装一个扩展,例如 篡改猴、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();
- })();