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