您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Visual indicator of page progress while scrolling
- // ==UserScript==
- // @license MIT
- // @name Scroll Page Progress
- // @namespace http://tampermonkey.net/
- // @version 1.8.3
- // @description Visual indicator of page progress while scrolling
- // @author You
- // @match *://*/*
- // @icon data:image/gif;base64,R0lGODlhAQABAAAAACH5BAEKAAEALAAAAAABAAEAAAICTAEAOw==
- // @grant none
- // ==/UserScript==
- (function () {
- 'use strict';
- const currentState = {
- deg: 0,
- progress: 0,
- zIndex: 0,
- movementIntervalId: null
- }
- let globalShadow
- let progressBar
- const createDiv = () => document.createElement('div')
- function insertCirculaProgressBarEl() {
- const shadowHost = createDiv()
- shadowHost.id = 'host-shwadow-circular-progress'
- const shadow = shadowHost.attachShadow({ mode: "closed" });
- globalShadow = shadow
- const circularProgressBar = createDiv()
- progressBar = circularProgressBar
- const contentWrapper = createDiv()
- const closeOverlay = createDiv()
- const title = createDiv()
- const overlay = createDiv()
- const leftSide = createDiv()
- const rightSide = createDiv()
- circularProgressBar.classList.add('circular-progress-bar')
- title.classList.add('title');
- closeOverlay.classList.add('close-overlay');
- contentWrapper.classList.add('content-wrapper')
- overlay.classList.add('overlay');
- leftSide.classList.add('left-side');
- rightSide.classList.add('right-side');
- title.innerText = '-%'
- // this is the only way to create a trusted HTML element
- if (window.trustedTypes) {
- closeOverlay.innerHTML = window.trustedTypes.defaultPolicy.createHTML('×')
- } else {
- closeOverlay.innerHTML = '×'
- }
- closeOverlay.addEventListener('click', function () {
- //circularProgressBar.style.display = 'none'
- const screenWidth = window.innerWidth;
- const elementoWidth = circularProgressBar.offsetWidth;
- // Calcular la nueva posición
- const newPosition = screenWidth - elementoWidth / 2;
- // Aplicar la nueva posición
- circularProgressBar.style.left = `${newPosition}px`;
- const topPosition = circularProgressBar.style.top
- const path = window.location.pathname;
- let savedPaths = JSON.parse(localStorage.getItem('not-allowed-paths')) || [];
- const existingEntryIndex = savedPaths.findIndex(entry => entry.path === path);
- const newEntry = {
- path: path,
- left: newPosition,
- top: topPosition || '0px' // Si el top no está definido, usa '0px' como valor por defecto
- };
- if (existingEntryIndex !== -1) {
- // Si ya existe una entrada para la ruta, actualiza la posición
- savedPaths[existingEntryIndex] = newEntry;
- } else {
- // Si no existe, añade la nueva entrada
- savedPaths.push(newEntry);
- }
- console.log("savedPaths", savedPaths)
- // Guardar el array actualizado en localStorage
- localStorage.setItem('not-allowed-paths', JSON.stringify(savedPaths));
- });
- ;[title, overlay, leftSide, rightSide].forEach(childEl => contentWrapper.appendChild(childEl))
- circularProgressBar.appendChild(closeOverlay)
- circularProgressBar.appendChild(contentWrapper)
- shadow.appendChild(circularProgressBar)
- document.body.appendChild(shadowHost)
- }
- function addCSS() {
- const styleSheet = document.createElement('style');
- styleSheet.textContent = `
- * {
- box-sizing: border-box;
- padding: 0;
- margin: 0;
- }
- .circular-progress-bar {
- --backgroundColor: #424242;
- --left-side-angle: 180deg;
- --barColor:orangered;
- width: 60px;
- height: 60px;
- color: #fff;
- border-radius: 50%;
- position: fixed;
- z-index: 2147483646;
- background: var(--backgroundColor);
- border: 5px solid white;
- box-shadow:
- 0 1px 1px hsl(0deg 0% 0% / 0.075),
- 0 2px 2px hsl(0deg 0% 0% / 0.075),
- 0 4px 4px hsl(0deg 0% 0% / 0.075),
- 0 8px 8px hsl(0deg 0% 0% / 0.075),
- 0 16px 16px hsl(0deg 0% 0% / 0.075);
- text-align: center;
- cursor: pointer;
- transition: opacity 0.2s ease;
- }
- .circular-progress-bar .overlay {
- width: 50%;
- height: 100%;
- position: absolute;
- top: 0;
- left: 0;
- background-color: var(--backgroundColor);
- transform-origin: right;
- transform: rotate(var(--overlay));
- }
- .close-overlay {
- width: 20px;
- height: 20px;
- position: absolute;
- left: 100%;
- top: -30%;
- z-index: 2147483647;
- display: flex;
- justify-content: center;
- align-items: center;
- border-radius: 50%;
- background: orangered;
- font-size: 19px;
- text-align: center;
- opacity: 0;
- }
- .close-overlay:hover {
- font-weight: bold;
- }
- .content-wrapper {
- overflow: hidden;
- height: 100%;
- width: 100%;
- border-radius: 50%;
- position: relative;
- }
- .circular-progress-bar:hover .close-overlay {
- opacity:1;
- }
- .circular-progress-bar .title {
- font-size: 15px;
- font-weight: bold;
- position:relative;
- height: 100%;
- display:flex;
- justify-content:center;
- align-items: center;
- z-index: 100;
- }
- .circular-progress-bar .left-side,
- .circular-progress-bar .right-side {
- width: 50%;
- height: 100%;
- position: absolute;
- top: 0;
- left: 0;
- border: 5px solid var(--barColor);
- border-radius: 100px 0px 0px 100px;
- border-right: 0;
- transform-origin: right;
- }
- .circular-progress-bar .left-side {
- transform: rotate(var(--left-side-angle));
- }
- .circular-progress-bar .right-side {
- transform: rotate(var(--right-side-angle));
- }
- `
- globalShadow.appendChild(styleSheet)
- }
- function setAngle(deg) {
- const progressBar = globalShadow.querySelector('.circular-progress-bar')
- const leftSide = globalShadow.querySelector('.left-side')
- const rightSide = globalShadow.querySelector('.right-side')
- const overlay = globalShadow.querySelector('.circular-progress-bar .overlay')
- const zIndex = deg > 180 ? 100 : 0
- const rightSideAngle = deg < 180 ? deg : 180
- const leftSideAngle = deg
- const overlayAngle = deg < 180 ? 0 : deg - 180
- const zIndexChangedToPositive = currentState.zIndex === 0 && zIndex === 100
- if (deg > 180) {
- rightSide.style.zIndex = 2
- leftSide.style.zIndex = 0
- overlay.style.zIndex = 1
- } else {
- rightSide.style.zIndex = 1
- leftSide.style.zIndex = 0
- overlay.style.zIndex = 2
- }
- progressBar.style.setProperty('--overlay', `${overlayAngle}deg`);
- progressBar.style.setProperty('--right-side-angle', `${rightSideAngle}deg`);
- progressBar.style.setProperty('--left-side-angle', `${leftSideAngle}deg`);
- }
- function smoothProgressBar(targetProgress, duration) {
- if (currentState.movementIntervalId) {
- clearInterval(currentState.movementIntervalId);
- }
- let currentProgress = currentState.deg
- const increment = (targetProgress - currentProgress) / (duration / 10);
- currentState.movementIntervalId = setInterval(function () {
- currentProgress += increment;
- if ((increment > 0 && currentProgress >= targetProgress) || (increment < 0 && currentProgress <= targetProgress)) {
- currentProgress = targetProgress;
- clearInterval(currentState.movementIntervalId);
- }
- setAngle(currentProgress)
- }, 10);
- }
- function percentageToAngle(percentageNumber) {
- if (percentageNumber > 100) {
- return 360
- }
- if (percentageNumber < 0) {
- return 0
- }
- return (360 * percentageNumber) / 100
- }
- function setPercentage(percentageNumber) {
- const angle = percentageToAngle(percentageNumber)
- smoothProgressBar(angle, 400)
- }
- function debounce(callback, wait) {
- let timerId;
- return (...args) => {
- clearTimeout(timerId);
- timerId = setTimeout(() => {
- callback(...args);
- }, wait);
- };
- }
- function setEventListeners() {
- let offsetX = 0, offsetY = 0, isDragging = false;
- progressBar.addEventListener("mousedown", (e) => {
- e.preventDefault()
- offsetX = e.clientX - progressBar.offsetLeft;
- offsetY = e.clientY - progressBar.offsetTop;
- isDragging = true;
- progressBar.style.cursor = "grabbing";
- progressBar.style.opacity = "0.3";
- });
- document.addEventListener("mousemove", (e) => {
- if (isDragging) {
- e.preventDefault();
- const left = e.clientX - offsetX;
- const top = e.clientY - offsetY;
- progressBar.style.left = `${left}px`;
- progressBar.style.top = `${top}px`;
- savePosition(left, top); // Guardar la posición cada vez que se mueve
- }
- });
- document.addEventListener("mouseup", () => {
- isDragging = false;
- progressBar.style.cursor = "grab";
- progressBar.style.opacity = "1";
- });
- }
- function savePosition(left, top) {
- const position = { left, top };
- localStorage.setItem("elementPosition", JSON.stringify(position));
- }
- function loadPosition() {
- const currentPath = window.location.pathname;
- const savedPaths = JSON.parse(localStorage.getItem('not-allowed-paths')) || [];
- const entry = savedPaths.find(entry => entry.path === currentPath);
- if (entry) {
- console.log('exist entry')
- // Si existe una entrada para la ruta, aplica las posiciones guardadas
- progressBar.style.left = `${entry.left}px`;
- progressBar.style.top = `${entry.top}`;
- return;
- }
- const savedPosition = localStorage.getItem("elementPosition");
- if (savedPosition) {
- const { left, top } = JSON.parse(savedPosition);
- progressBar.style.left = `${left}px`;
- progressBar.style.top = `${top}px`;
- } else {
- progressBar.style.right = `10px`;
- progressBar.style.top = `10px`;
- }
- }
- function getCurrentScrollProgress() {
- const winScroll = document.body.scrollTop || document.documentElement.scrollTop;
- const height = document.documentElement.scrollHeight - document.documentElement.clientHeight;
- const progress = (winScroll / height) * 100;
- return Math.trunc(progress);
- }
- function watchScroll() {
- const progressBarTitle = globalShadow.querySelector('.title')
- document.addEventListener('scroll', debounce(() => {
- setPercentage(getCurrentScrollProgress())
- progressBarTitle.innerText = getCurrentScrollProgress() + '%'
- currentState.progress = getCurrentScrollProgress()
- currentState.deg = percentageToAngle(getCurrentScrollProgress())
- }, 50))
- }
- document.onreadystatechange = function () {
- if (document.readyState == "complete") {
- if (window.trustedTypes && window.trustedTypes.createPolicy && !window.trustedTypes.defaultPolicy) {
- window.trustedTypes.createPolicy('default', {
- createHTML: (string, sink) => string
- });
- }
- insertCirculaProgressBarEl()
- setEventListeners()
- addCSS()
- loadPosition()
- watchScroll()
- }
- }
- })();