Greasy Fork

Greasy Fork is available in English.

网页定时刷新

网页定时刷新工具

当前为 2025-11-09 提交的版本,查看 最新版本

您需要先安装一个扩展,例如 篡改猴Greasemonkey暴力猴,之后才能安装此脚本。

You will need to install an extension such as Tampermonkey to install this script.

您需要先安装一个扩展,例如 篡改猴暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴Userscripts ,之后才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。

您需要先安装用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name         网页定时刷新
// @namespace    http://tampermonkey.net/
// @version      1.0
// @description  网页定时刷新工具
// @author       huihuia24
// @match        *://*/*
// @grant        GM_setValue
// @grant        GM_getValue
// @noframes
// @homepage     https://github.com/huihuia24
// @source       https://github.com/huihuia24/Web-page-auto-refresh
// @icon         https://pic.uzzf.com/up/2025-5/2025591617247637.png
// ==/UserScript==

(function() {
    'use strict';

    // 单实例防护
    const UI_ID = 'fullDragRefreshTool';
    if (document.getElementById(UI_ID)) return;
    
    const SITE_KEY = window.location.hostname;
    const INSTANCE_KEY = `${SITE_KEY}_refresh_instance`;
    if (GM_getValue(INSTANCE_KEY, false)) return;
    GM_setValue(INSTANCE_KEY, true);

    // 状态存储
    const STATE_KEY = {
        isEnabled: `${SITE_KEY}_is_enabled`,
        interval: `${SITE_KEY}_interval`,
        unit: `${SITE_KEY}_unit`,
        lastStartTime: `${SITE_KEY}_last_start`,
        position: `${SITE_KEY}_ui_position`
    };

    // 恢复状态
    let isEnabled = GM_getValue(STATE_KEY.isEnabled, false);
    let currentUnit = GM_getValue(STATE_KEY.unit, 's');
    let intervalSeconds = GM_getValue(STATE_KEY.interval, 60);
    let lastStartTime = GM_getValue(STATE_KEY.lastStartTime, Date.now());
    let remainingSeconds = 0;
    let timer = null;
    let isDragging = false;
    let offsetX, offsetY;

    // 计算剩余时间
    function calculateRemaining() {
        if (!isEnabled) return intervalSeconds;
        const now = Date.now();
        const elapsed = Math.floor((now - lastStartTime) / 1000);
        return Math.max(0, intervalSeconds - elapsed);
    }
    remainingSeconds = calculateRemaining();

    // 单位转换
    function convertToSeconds(value, unit) {
        switch(unit) {
            case 'h': return value * 3600;
            case 'm': return value * 60;
            case 's': default: return value;
        }
    }
    function convertFromSeconds(seconds, unit) {
        switch(unit) {
            case 'h': return Math.floor(seconds / 3600);
            case 'm': return Math.floor(seconds / 60);
            case 's': default: return seconds;
        }
    }
    function formatTime(seconds) {
        if (seconds < 60) return `${seconds}s`;
        const m = Math.floor(seconds / 60);
        const s = seconds % 60;
        return `${m}m${s}s`;
    }

    // 创建全屏拖拽UI(交互优化版)
    function createUI() {
        // 主容器(整个区域可拖拽)
        const container = document.createElement('div');
        container.id = UI_ID;
        container.style.cssText = `
            position: fixed;
            background: white;
            border: 1px solid #e2e8f0;
            border-radius: 12px;
            padding: 12px 16px;
            box-shadow: 0 6px 18px rgba(0, 0, 0, 0.08);
            z-index: 99999;
            font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
            font-size: 14px;
            display: flex;
            align-items: center;
            gap: 12px;
            user-select: none;
            transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
            cursor: default; /* 保持默认指针样式 */
        `;

        // 恢复位置
        const savedPos = GM_getValue(STATE_KEY.position, { top: '20px', left: '20px' });
        container.style.top = savedPos.top;
        container.style.left = savedPos.left;

        // 图标区域(视觉强化)
        const iconArea = document.createElement('div');
        iconArea.style.cssText = `
            width: 32px;
            height: 32px;
            border-radius: 8px;
            background: linear-gradient(135deg, #6366f1, #8b5cf6);
            display: flex;
            align-items: center;
            justify-content: center;
            transition: all 0.2s ease;
        `;
        iconArea.innerHTML = `
            <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="white" stroke-width="2" width="18" height="18">
                <path d="M23 4v6h-6"/>
                <path d="M1 20v-6h6"/>
                <path d="M3.51 9a9 9 0 0 1 14.85-3.36L23 10M1 14l4.64 4.36A9 9 0 0 0 20.49 15"/>
            </svg>
        `;

        // 标题
        const title = document.createElement('span');
        title.textContent = '定时刷新 =';
        title.style.fontWeight = '500';
        title.style.color = '#1e293b';

        // 输入框(现代风格)
        const input = document.createElement('input');
        input.type = 'number';
        input.value = convertFromSeconds(intervalSeconds, currentUnit);
        input.min = currentUnit === 's' ? 5 : 1;
        input.style.cssText = `
            width: 50px;
            padding: 6px 10px;
            border: 1px solid #e2e8f0;
            border-radius: 8px;
            font-size: 14px;
            outline: none;
            transition: border-color 0.2s ease;
        `;
        input.addEventListener('focus', () => {
            input.style.borderColor = '#a5b4fc';
        });
        input.addEventListener('blur', () => {
            input.style.borderColor = '#e2e8f0';
        });

        // 单位选择(下拉增强)
        const unitSelect = document.createElement('select');
        unitSelect.style.cssText = `
            padding: 6px 10px;
            border: 1px solid #e2e8f0;
            border-radius: 8px;
            background: white;
            font-size: 14px;
            color: #475569;
            cursor: pointer;
            outline: none;
            appearance: none;
            background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%2394a3b8' stroke-width='2' stroke-linecap='round' stroke-linejoin='round' width='12' height='12'%3E%3Cpath d='M6 9l6 6 6-6'/%3E%3C/svg%3E");
            background-repeat: no-repeat;
            background-position: right 10px center;
            background-size: 12px;
            transition: border-color 0.2s ease;
        `;
        ['s', 'm', 'h'].forEach(unit => {
            const option = document.createElement('option');
            option.value = unit;
            option.textContent = { s: '秒', m: '分', h: '时' }[unit];
            option.selected = unit === currentUnit;
            unitSelect.appendChild(option);
        });
        unitSelect.addEventListener('focus', () => {
            unitSelect.style.borderColor = '#a5b4fc';
        });
        unitSelect.addEventListener('blur', () => {
            unitSelect.style.borderColor = '#e2e8f0';
        });

        // 立体控制按钮(渐变+悬停动画)
        const controlBtn = document.createElement('button');
        controlBtn.style.cssText = `
            display: flex;
            align-items: center;
            gap: 6px;
            padding: 8px 16px;
            border: none;
            border-radius: 8px;
            color: white;
            cursor: pointer;
            font-size: 14px;
            font-weight: 500;
            background: ${isEnabled ? 'linear-gradient(135deg, #f87171, #ef4444)' : 'linear-gradient(135deg, #6366f1, #8b5cf6)'};
            box-shadow: 0 4px 8px rgba(0, 0, 0, 0.1);
            transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
        `;
        controlBtn.innerHTML = isEnabled 
            ? `
                <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="white" stroke-width="2" width="16" height="16">
                    <rect x="6" y="4" width="4" height="16"/>
                    <rect x="14" y="4" width="4" height="16"/>
                </svg>
                停止
            ` 
            : `
                <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="white" stroke-width="2" width="16" height="16">
                    <polygon points="5 3 19 12 5 21 5 3"/>
                </svg>
                启动
            `;
        controlBtn.addEventListener('mouseenter', () => {
            controlBtn.style.transform = 'translateY(-2px)';
            controlBtn.style.boxShadow = isEnabled 
                ? '0 6px 12px rgba(239, 68, 68, 0.3)' 
                : '0 6px 12px rgba(99, 102, 241, 0.3)';
        });
        controlBtn.addEventListener('mouseleave', () => {
            controlBtn.style.transform = 'translateY(0)';
            controlBtn.style.boxShadow = isEnabled 
                ? '0 4px 8px rgba(239, 68, 68, 0.1)' 
                : '0 4px 8px rgba(99, 102, 241, 0.1)';
        });

        // 状态显示(动态变化)
        const statusText = document.createElement('span');
        statusText.textContent = isEnabled ? `(剩余: ${formatTime(remainingSeconds)})` : '(已停止)';
        statusText.style.cssText = `
            color: #64748b;
            font-size: 13px;
            min-width: 90px;
            text-align: center;
            font-family: monospace;
            transition: all 0.3s ease;
        `;

        // 组装UI
        container.appendChild(iconArea);
        container.appendChild(title);
        container.appendChild(input);
        container.appendChild(unitSelect);
        container.appendChild(controlBtn);
        container.appendChild(statusText);
        document.body.appendChild(container);

        // 全屏拖拽功能实现(整个UI区域可拖拽)
        container.addEventListener('mousedown', (e) => {
            isDragging = true;
            const rect = container.getBoundingClientRect();
            offsetX = e.clientX - rect.left;
            offsetY = e.clientY - rect.top;
            container.style.transition = 'none';
            container.style.zIndex = '999999';
            container.style.boxShadow = '0 8px 24px rgba(0, 0, 0, 0.15)';
            e.preventDefault();
        });

        document.addEventListener('mousemove', (e) => {
            if (!isDragging) return;
            const newLeft = e.clientX - offsetX;
            const newTop = e.clientY - offsetY;
            const maxLeft = window.innerWidth - container.offsetWidth - 20;
            const maxTop = window.innerHeight - container.offsetHeight - 20;
            
            container.style.left = `${Math.max(20, Math.min(newLeft, maxLeft))}px`;
            container.style.top = `${Math.max(20, Math.min(newTop, maxTop))}px`;
        });

        document.addEventListener('mouseup', () => {
            if (isDragging) {
                isDragging = false;
                container.style.transition = 'all 0.3s cubic-bezier(0.4, 0, 0.2, 1)';
                container.style.zIndex = '99999';
                container.style.boxShadow = '0 6px 18px rgba(0, 0, 0, 0.08)';
                GM_setValue(STATE_KEY.position, {
                    top: container.style.top,
                    left: container.style.left
                });
            }
        });

        // 其他事件绑定
        input.addEventListener('change', () => {
            const value = Math.max(parseInt(input.value, 10) || 1, input.min);
            intervalSeconds = convertToSeconds(value, currentUnit);
            GM_setValue(STATE_KEY.interval, intervalSeconds);
            
            if (isEnabled) {
                restartTimer();
            } else {
                remainingSeconds = intervalSeconds;
                statusText.textContent = `(剩余: ${formatTime(remainingSeconds)})`;
            }
        });

        unitSelect.addEventListener('change', () => {
            currentUnit = unitSelect.value;
            input.value = convertFromSeconds(intervalSeconds, currentUnit);
            input.min = currentUnit === 's' ? 5 : 1;
            GM_setValue(STATE_KEY.unit, currentUnit);
            
            if (isEnabled) {
                restartTimer();
            } else {
                remainingSeconds = intervalSeconds;
                statusText.textContent = `(剩余: ${formatTime(remainingSeconds)})`;
            }
        });

        controlBtn.addEventListener('click', () => {
            if (isEnabled) {
                stopTimer();
                controlBtn.innerHTML = `
                    <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="white" stroke-width="2" width="16" height="16">
                        <polygon points="5 3 19 12 5 21 5 3"/>
                    </svg>
                    启动
                `;
                controlBtn.style.background = 'linear-gradient(135deg, #6366f1, #8b5cf6)';
                statusText.textContent = '(已停止)';
            } else {
                startTimer();
                controlBtn.innerHTML = `
                    <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="none" stroke="white" stroke-width="2" width="16" height="16">
                        <rect x="6" y="4" width="4" height="16"/>
                        <rect x="14" y="4" width="4" height="16"/>
                    </svg>
                    停止
                `;
                controlBtn.style.background = 'linear-gradient(135deg, #f87171, #ef4444)';
            }
        });

        return { container, controlBtn, statusText, input, unitSelect, iconArea };
    }

    // 计时器控制
    function startTimer() {
        isEnabled = true;
        lastStartTime = Date.now();
        GM_setValue(STATE_KEY.isEnabled, true);
        GM_setValue(STATE_KEY.lastStartTime, lastStartTime);

        if (timer) clearInterval(timer);
        timer = setInterval(() => {
            remainingSeconds--;
            uiElements.statusText.textContent = `(剩余: ${formatTime(remainingSeconds)})`;

            if (remainingSeconds <= 0) {
                clearInterval(timer);
                GM_setValue(STATE_KEY.lastStartTime, Date.now());
                window.location.reload();
            }
        }, 1000);
    }

    function stopTimer() {
        if (timer) clearInterval(timer);
        timer = null;
        isEnabled = false;
        GM_setValue(STATE_KEY.isEnabled, false);
    }

    function restartTimer() {
        stopTimer();
        startTimer();
    }

    // 初始化
    const uiElements = createUI();

    // 自动启动
    if (isEnabled) {
        startTimer();
    }

    // 页面卸载清理
    window.addEventListener('beforeunload', () => {
        GM_setValue(INSTANCE_KEY, false);
    });
})();