scroll-button

scroll-button (Mobile Optimized: Smaller & Auto-Fade)

Verzia zo dňa 17.12.2025. Pozri najnovšiu verziu.

Na nainštalovanie skriptu si budete musieť nainštalovať rozšírenie, ako napríklad Tampermonkey, Greasemonkey alebo Violentmonkey.

Na inštaláciu tohto skriptu je potrebné nainštalovať rozšírenie, ako napríklad Tampermonkey.

Na nainštalovanie skriptu si budete musieť nainštalovať rozšírenie, ako napríklad Tampermonkey, % alebo Violentmonkey.

Na nainštalovanie skriptu si budete musieť nainštalovať rozšírenie, ako napríklad Tampermonkey alebo Userscripts.

Na inštaláciu tohto skriptu je potrebné nainštalovať rozšírenie, ako napríklad Tampermonkey.

Na inštaláciu tohto skriptu je potrebné nainštalovať rozšírenie správcu používateľských skriptov.

(Už mám správcu používateľských skriptov, nechajte ma ho nainštalovať!)

Na inštaláciu tohto štýlu je potrebné nainštalovať rozšírenie, ako napríklad Stylus.

Na inštaláciu tohto štýlu je potrebné nainštalovať rozšírenie, ako napríklad Stylus.

Na inštaláciu tohto štýlu je potrebné nainštalovať rozšírenie, ako napríklad Stylus.

Na inštaláciu tohto štýlu je potrebné nainštalovať rozšírenie správcu používateľských štýlov.

Na inštaláciu tohto štýlu je potrebné nainštalovať rozšírenie správcu používateľských štýlov.

Na inštaláciu tohto štýlu je potrebné nainštalovať rozšírenie správcu používateľských štýlov.

(Už mám správcu používateľských štýlov, nechajte ma ho nainštalovať!)

// ==UserScript==
// @name         scroll-button
// @namespace    https://github.com/livinginpurple
// @version      20251217.03
// @description  scroll-button (Mobile Optimized: Smaller & Auto-Fade)
// @license      WTFPL
// @author       livinginpurple
// @include      *
// @run-at       document-end
// @grant        none
// ==/UserScript==

(() => {
    'use strict';

    const init = () => {
        const btnId = 'gamma-scroll-btn';
        if (document.getElementById(btnId)) return;

        // SVG 圖示 (保持不變)
        const icons = {
            up: `<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" fill="white" viewBox="0 0 16 16"><path fill-rule="evenodd" d="M8 12a.5.5 0 0 0 .5-.5V5.707l2.146 2.147a.5.5 0 0 0 .708-.708l-3-3a.5.5 0 0 0-.708 0l-3 3a.5.5 0 1 0 .708.708L7.5 5.707V11.5a.5.5 0 0 0 .5.5z"/></svg>`,
            down: `<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" fill="white" viewBox="0 0 16 16"><path fill-rule="evenodd" d="M8 4a.5.5 0 0 1 .5.5v5.793l2.146-2.147a.5.5 0 0 1 .708.708l-3 3a.5.5 0 0 1-.708 0l-3-3a.5.5 0 1 1 .708-.708L7.5 10.293V4.5A.5.5 0 0 1 8 4z"/></svg>`
        };

        const btn = document.createElement('button');
        btn.id = btnId;
        btn.type = 'button';
        btn.setAttribute('aria-label', 'Scroll navigation');

        // 定義樣式 (調整大小與加入透明度過渡)
        const styles = {
            position: 'fixed',
            bottom: '20px',
            right: '20px',
            width: '40px',     // 縮小尺寸
            height: '40px',    // 縮小尺寸
            borderRadius: '50%',
            backgroundColor: '#0d6efd',
            border: 'none',
            boxShadow: '0 0.2rem 0.5rem rgba(0,0,0,0.3)',
            zIndex: '10000',
            cursor: 'pointer',
            display: 'flex',
            justifyContent: 'center',
            alignItems: 'center',
            // 增加 opacity 的過渡效果
            transition: 'transform 0.2s, background-color 0.2s, opacity 0.5s ease-in-out',
            touchAction: 'manipulation',
            padding: '0',
            opacity: '0.3' // 預設為半透明 (腳本啟動時)
        };

        Object.assign(btn.style, styles);

        // Hover & Active 互動
        btn.onmouseenter = () => { btn.style.transform = 'scale(1.1)'; activeHandler(); };
        btn.onmouseleave = () => { btn.style.transform = 'scale(1)'; };
        btn.onmousedown = () => { btn.style.backgroundColor = '#0b5ed7'; activeHandler(); };
        btn.onmouseup = () => { btn.style.backgroundColor = '#0d6efd'; };
        // 針對手機觸摸也視為「活躍」
        btn.ontouchstart = () => { activeHandler(); };

        document.body.appendChild(btn);

        const State = { TOP: 'top', SCROLLED: 'scrolled' };

        // --- 透明度控制邏輯 ---
        let idleTimer = null;
        const activeHandler = () => {
            // 1. 動作時:顯示按鈕 (不透明)
            btn.style.opacity = '1';
            
            // 2. 重置計時器
            if (idleTimer) clearTimeout(idleTimer);

            // 3. 設定閒置 2 秒後變淡
            idleTimer = setTimeout(() => {
                btn.style.opacity = '0.3'; // 淡出目標透明度
            }, 2000);
        };

        const updateButtonState = () => {
            activeHandler(); // 捲動視為活躍動作

            const scrollTop = window.scrollY || document.documentElement.scrollTop;
            const isAtTop = scrollTop < 50;

            const nextState = isAtTop ? State.TOP : State.SCROLLED;
            if (btn.dataset.state === nextState) return;

            if (isAtTop) {
                btn.innerHTML = icons.down;
                btn.dataset.state = State.TOP;
            } else {
                btn.innerHTML = icons.up;
                btn.dataset.state = State.SCROLLED;
            }
        };

        const handleScrollAction = (e) => {
            e.preventDefault();
            activeHandler(); // 點擊視為活躍動作
            
            if (btn.dataset.state === State.TOP) {
                window.scrollTo({ top: document.body.scrollHeight, behavior: 'smooth' });
            } else {
                window.scrollTo({ top: 0, behavior: 'smooth' });
            }
        };

        btn.addEventListener('click', handleScrollAction);
        window.addEventListener('scroll', updateButtonState, { passive: true });
        
        // 初始執行
        updateButtonState();
    };

    if (document.readyState === 'loading') {
        document.addEventListener('DOMContentLoaded', init);
    } else {
        init();
    }
})();