scroll-button

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

Per 17-12-2025. Zie de nieuwste versie.

Voor het installeren van scripts heb je een extensie nodig, zoals Tampermonkey, Greasemonkey of Violentmonkey.

Voor het installeren van scripts heb je een extensie nodig, zoals {tampermonkey_link:Tampermonkey}.

Voor het installeren van scripts heb je een extensie nodig, zoals Tampermonkey of Violentmonkey.

Voor het installeren van scripts heb je een extensie nodig, zoals Tampermonkey of Userscripts.

Voor het installeren van scripts heb je een extensie nodig, zoals {tampermonkey_link:Tampermonkey}.

Voor het installeren van scripts heb je een gebruikersscriptbeheerder nodig.

(Ik heb al een user script manager, laat me het downloaden!)

Voor het installeren van gebruikersstijlen heb je een extensie nodig, zoals {stylus_link:Stylus}.

Voor het installeren van gebruikersstijlen heb je een extensie nodig, zoals {stylus_link:Stylus}.

Voor het installeren van gebruikersstijlen heb je een extensie nodig, zoals {stylus_link:Stylus}.

Voor het installeren van gebruikersstijlen heb je een gebruikersstijlbeheerder nodig.

Voor het installeren van gebruikersstijlen heb je een gebruikersstijlbeheerder nodig.

Voor het installeren van gebruikersstijlen heb je een gebruikersstijlbeheerder nodig.

(Ik heb al een beheerder - laat me doorgaan met de installatie!)

// ==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();
    }
})();