scroll-button (Mobile Optimized: Smaller & Auto-Fade)
// ==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();
}
})();