悬浮球全屏按钮(小巧+边界限制+透明)

小号悬浮球按钮,点击切换全屏,右键/长按隐藏,自动限制边界,保持可见,高透明度美化!在没有键盘的设备上仍然可以随时“全屏显示”,让平板、手机的显示空间不再被约束!

// ==UserScript==
// @name         悬浮球全屏按钮(小巧+边界限制+透明)
// @namespace    https://muyyy.link/
// @version      1.5
// @description  小号悬浮球按钮,点击切换全屏,右键/长按隐藏,自动限制边界,保持可见,高透明度美化!在没有键盘的设备上仍然可以随时“全屏显示”,让平板、手机的显示空间不再被约束!
// @author       Muyu
// @homepage     https://muyyy.link/
// @license      Apache-2.0
// @match        *://*/*
// @grant        none
// ==/UserScript==

(function () {
    'use strict';

    const ball = document.createElement('div');
    ball.innerText = '⛶';
    Object.assign(ball.style, {
        position: 'fixed',
        bottom: '20px',
        right: '20px',
        width: '36px',
        height: '36px',
        borderRadius: '50%',
        backgroundColor: 'rgba(0, 0, 0, 0.25)',
        color: 'white',
        display: 'flex',
        alignItems: 'center',
        justifyContent: 'center',
        fontSize: '18px',
        zIndex: 99999,
        cursor: 'grab',
        userSelect: 'none',
        transition: 'opacity 0.3s',
    });

    document.body.appendChild(ball);

    function isFullscreen() {
        return document.fullscreenElement || document.webkitFullscreenElement || document.mozFullScreenElement || document.msFullscreenElement;
    }

    function enterFullscreen() {
        const el = document.documentElement;
        if (el.requestFullscreen) el.requestFullscreen();
        else if (el.webkitRequestFullscreen) el.webkitRequestFullscreen();
        else if (el.mozRequestFullScreen) el.mozRequestFullScreen();
        else if (el.msRequestFullscreen) el.msRequestFullscreen();
    }

    function exitFullscreen() {
        if (document.exitFullscreen) document.exitFullscreen();
        else if (document.webkitExitFullscreen) document.webkitExitFullscreen();
        else if (document.mozCancelFullScreen) document.mozCancelFullScreen();
        else if (document.msExitFullscreen) document.msExitFullscreen();
    }

    ball.addEventListener('click', () => {
        if (isDragging) return;
        if (isFullscreen()) {
            exitFullscreen();
        } else {
            enterFullscreen();
        }
    });

    ball.addEventListener('contextmenu', (e) => {
        e.preventDefault();
        ball.style.display = 'none';
    });

    // 长按隐藏
    let pressTimer = null;
    let longPressTriggered = false;

    ball.addEventListener('touchstart', (e) => {
        isDragging = false;
        longPressTriggered = false;
        pressTimer = setTimeout(() => {
            longPressTriggered = true;
            ball.style.display = 'none';
        }, 800);
    });

    ball.addEventListener('touchend', () => {
        if (pressTimer) clearTimeout(pressTimer);
    });

    ball.addEventListener('touchmove', () => {
        if (pressTimer) {
            clearTimeout(pressTimer);
            pressTimer = null;
        }
    });

    // 拖动逻辑
    let isDragging = false;
    let isDraggingFromBall = false;
    let offsetX, offsetY;

    function clamp(val, min, max) {
        return Math.max(min, Math.min(val, max));
    }

    function moveBall(x, y) {
        const margin = 5;
        const maxX = window.innerWidth - ball.offsetWidth - margin;
        const maxY = window.innerHeight - ball.offsetHeight - margin;

        const newX = clamp(x, margin, maxX);
        const newY = clamp(y, margin, maxY);

        ball.style.left = `${newX}px`;
        ball.style.top = `${newY}px`;
        ball.style.right = 'auto';
        ball.style.bottom = 'auto';
    }

    // 鼠标拖动
    ball.addEventListener('mousedown', (e) => {
        if (e.button !== 0) return;
        isDragging = true;
        isDraggingFromBall = true;
        ball.style.cursor = 'grabbing';
        offsetX = e.clientX - ball.getBoundingClientRect().left;
        offsetY = e.clientY - ball.getBoundingClientRect().top;
        e.preventDefault();
    });

    document.addEventListener('mousemove', (e) => {
        if (!isDragging || !isDraggingFromBall) return;
        moveBall(e.clientX - offsetX, e.clientY - offsetY);
    });

    document.addEventListener('mouseup', () => {
        if (isDraggingFromBall) {
            isDragging = false;
            isDraggingFromBall = false;
            ball.style.cursor = 'grab';
        }
    });

    // 触摸拖动
    ball.addEventListener('touchstart', (e) => {
        isDragging = true;
        isDraggingFromBall = true;
        const touch = e.touches[0];
        offsetX = touch.clientX - ball.getBoundingClientRect().left;
        offsetY = touch.clientY - ball.getBoundingClientRect().top;
    });

    document.addEventListener('touchmove', (e) => {
        if (!isDragging || !isDraggingFromBall) return;
        const touch = e.touches[0];
        moveBall(touch.clientX - offsetX, touch.clientY - offsetY);
        e.preventDefault();
    }, { passive: false });

    document.addEventListener('touchend', () => {
        isDragging = false;
        isDraggingFromBall = false;
    });
})();