// ==UserScript==
// @name 全屏按钮(适用于移动设备)
// @name:en Full screen button (for mobile devices)
// @namespace http://tampermonkey.net/
// @version 2.1
// @description 一个全屏按钮,支持拖动、自动淡化、防穿透、适配移动端横竖屏变化。
// @description:zh-CN 一个全屏按钮,支持拖动、自动淡化、防穿透、适配移动端横竖屏变化。
// @description:en A full-screen button, Supports dragging, automatic fading, anti-penetration, and adapts to the horizontal and vertical screen changes of mobile terminals.
// @author 凡留钰 + ChatGPT
// @contributor Gemini(某位(忘了额)网友用Gemini做过)
// @match *://*/*
// @icon https://greasyfork.s3.us-east-2.amazonaws.com/4eb17e88irkc3910fvbpp4f0h270
// @grant none
// @noframes
// @license MIT
// ==/UserScript==
(function () {
'use strict';
const btn = document.createElement('button');
let leftPercent = 95;
let topPercent = 95;
Object.assign(btn.style, {
position: 'fixed',
width: '4vw', // 按钮大小_百分比
height: '4vw',
minWidth: '24px', // 按钮大小_最小
minHeight: '24px',
maxWidth: '48px', // 按钮大小_最大
maxHeight: '48px',
backgroundImage: 'url("https://greasyfork.s3.us-east-2.amazonaws.com/4eb17e88irkc3910fvbpp4f0h270")',
backgroundSize: 'cover',
backgroundRepeat: 'no-repeat',
backgroundPosition: 'center',
borderRadius: '50%',
boxShadow: '0 2px 5px rgba(0,0,0,0.3)',
border: 'none',
borderRadius: '50%',
zIndex: '999',
cursor: 'grab',
userSelect: 'none',
transition: 'opacity 0.3s ease',
opacity: '1',
});
document.body.appendChild(btn);
function updatePosition() {
const width = btn.offsetWidth;
const height = btn.offsetHeight;
const left = (leftPercent / 100) * (window.innerWidth - width);
const top = (topPercent / 100) * (window.innerHeight - height);
btn.style.left = `${left}px`;
btn.style.top = `${top}px`;
}
function clampAndUpdatePosition() {
leftPercent = Math.max(0, Math.min(100, leftPercent));
topPercent = Math.max(0, Math.min(100, topPercent));
updatePosition();
}
function isFullscreen() {
return document.fullscreenElement || document.webkitFullscreenElement;
}
function toggleFullscreen() {
const el = document.documentElement;
if (!isFullscreen()) {
(el.requestFullscreen || el.webkitRequestFullscreen)?.call(el);
} else {
(document.exitFullscreen || document.webkitExitFullscreen)?.call(document);
}
}
let dragging = false, moved = false;
let offsetX = 0, offsetY = 0, startX = 0, startY = 0;
function dragStart(e) {
e.preventDefault();
dragging = true;
moved = false;
const x = e.touches ? e.touches[0].clientX : e.clientX;
const y = e.touches ? e.touches[0].clientY : e.clientY;
const rect = btn.getBoundingClientRect();
offsetX = x - rect.left;
offsetY = y - rect.top;
startX = x;
startY = y;
btn.style.cursor = 'grabbing';
clearHideTimer();
const moveEvt = e.type === 'touchstart' ? 'touchmove' : 'mousemove';
const endEvt = e.type === 'touchstart' ? 'touchend' : 'mouseup';
document.addEventListener(moveEvt, dragMove, { passive: false });
document.addEventListener(endEvt, dragEnd);
}
function dragMove(e) {
if (!dragging) return;
e.preventDefault();
const x = e.touches ? e.touches[0].clientX : e.clientX;
const y = e.touches ? e.touches[0].clientY : e.clientY;
if (Math.abs(x - startX) > 5 || Math.abs(y - startY) > 5) moved = true;
const newLeft = x - offsetX;
const newTop = y - offsetY;
const width = btn.offsetWidth;
const height = btn.offsetHeight;
leftPercent = (newLeft / (window.innerWidth - width)) * 100;
topPercent = (newTop / (window.innerHeight - height)) * 100;
clampAndUpdatePosition();
}
function dragEnd(e) {
dragging = false;
btn.style.cursor = 'grab';
document.removeEventListener('mousemove', dragMove);
document.removeEventListener('mouseup', dragEnd);
document.removeEventListener('touchmove', dragMove);
document.removeEventListener('touchend', dragEnd);
if (!moved) {
e.preventDefault();
toggleFullscreen();
}
startHideTimer();
}
btn.addEventListener('mousedown', dragStart);
btn.addEventListener('touchstart', dragStart, { passive: false });
btn.addEventListener('click', (e) => {
e.stopPropagation();
e.preventDefault();
});
let hideTimer;
function startHideTimer() {
clearTimeout(hideTimer);
hideTimer = setTimeout(() => {
btn.style.opacity = '0.4';
}, 3000);
}
function clearHideTimer() {
clearTimeout(hideTimer);
btn.style.opacity = '0.8';
}
['mouseenter', 'touchstart'].forEach(evt => btn.addEventListener(evt, clearHideTimer));
['mouseleave', 'touchend'].forEach(evt => btn.addEventListener(evt, startHideTimer));
window.addEventListener('resize', clampAndUpdatePosition);
window.addEventListener('orientationchange', clampAndUpdatePosition);
clampAndUpdatePosition();
startHideTimer();
})();