// ==UserScript== // @name Nav Scroll for safari // @namespace https://staybrowser.com/ // @version 0.1 // @description Template userscript created by Stay // @author You // @match *://*/* // @grant none // ==/UserScript== // Adaptive Mobile Nav (Improved with Darker Background Opacity) (function() { 'use strict'; let scrolling = false; let scrollDirection = 'down'; // スクロール方向:'down'(下)または'up'(上) let topSpeedCounter = 0; // 上部速度カウンタ(0: 1px, 1: 2px) let bottomSpeedCounter = 0; // 下部速度カウンタ(0: 1px, 1: 2px) let lastClickTime = 0; // デバウンス用タイムスタンプ const navBar = document.createElement('div'); navBar.setAttribute('translate', 'no'); navBar.style.cssText = ` position: fixed; bottom: 20px; right: 20px; background: rgba(0,0,0,0.9); border-radius: 10px; padding: 10px; z-index: 9999; color: white; opacity: 0.8; transition: opacity 0.5s; translate: no; `; navBar.innerHTML = ` `; document.body.appendChild(navBar); // ボタンスタイル(濃紫:#6A5ACD、文字:#E6E6FA、固定サイズ、固定フォント) const buttons = navBar.querySelectorAll('button'); buttons.forEach(btn => { btn.style.cssText = ` margin: 5px; padding: 5px; background: #6A5ACD; color: #E6E6FA; border: none; border-radius: 5px; width: 60px; height: 30px; box-sizing: border-box; font-family: -apple-system, San Francisco, sans-serif !important; font-size: 14px !important; line-height: 20px; text-align: center; translate: no; `; }); // デバウンス関数(連打防止) const debounce = (func, wait) => { return function() { const now = Date.now(); if (now - lastClickTime < wait) return; lastClickTime = now; func.apply(this, arguments); }; }; // 戻る(シンプル) const backBtn = navBar.querySelector('#backBtn'); backBtn.onclick = debounce(() => { history.back(); }, 200); // 上部へ(速度変更+Google検索対応) const scrollTopBtn = navBar.querySelector('#scrollTopBtn'); scrollTopBtn.onclick = debounce(() => { const targets = [ document.documentElement, document.body, document.querySelector('#main'), document.querySelector('.srp'), document.querySelector('[jscontroller]') ].filter(el => el); targets.forEach(target => { target.scrollTo({ top: 0, behavior: 'smooth' }); }); window.scrollTo({ top: 0, behavior: 'smooth' }); if (scrolling) { scrollDirection = 'up'; topSpeedCounter = (topSpeedCounter + 1) % 2; // 0→1→0 } }, 200); // 下部へ(速度変更+Google検索対応) const scrollBottomBtn = navBar.querySelector('#scrollBottomBtn'); scrollBottomBtn.onclick = debounce(() => { const targets = [ document.documentElement, document.body, document.querySelector('#main'), document.querySelector('.srp'), document.querySelector('[jscontroller]') ].filter(el => el); const maxHeight = Math.max( document.body.scrollHeight, document.documentElement.scrollHeight ); targets.forEach(target => { target.scrollTo({ top: maxHeight, behavior: 'smooth' }); }); window.scrollTo({ top: maxHeight, behavior: 'smooth' }); if (scrolling) { scrollDirection = 'down'; bottomSpeedCounter = (bottomSpeedCounter + 1) % 2; // 0→1→0 } }, 200); // 自動スクロール(方向+速度対応) const scrollBtn = navBar.querySelector('#scrollBtn'); scrollBtn.onclick = debounce(() => { if (scrollBtn.textContent === '停止') { scrolling = false; scrollBtn.textContent = '開始'; return; } scrolling = true; scrollDirection = 'down'; topSpeedCounter = 0; // 速度リセット bottomSpeedCounter = 0; scrollBtn.textContent = '停止'; const scroll = () => { if (scrolling) { const speed = scrollDirection === 'down' ? (bottomSpeedCounter === 0 ? 1 : 2) : (topSpeedCounter === 0 ? 1 : 2); window.scrollBy(0, scrollDirection === 'down' ? speed : -speed); requestAnimationFrame(scroll); } }; scroll(); }, 200); // スクロールトリガー(フェードイン/アウト) window.addEventListener('scroll', () => { navBar.style.opacity = window.scrollY > 200 ? '0.8' : '0.6'; }); // 動的DOM対応(Googleの無限スクロールやSPA対応) const observer = new MutationObserver(() => { if (!document.body.contains(navBar)) { document.body.appendChild(navBar); } }); observer.observe(document.body, { childList: true, subtree: true }); })();