字幕遮挡条

毛玻璃效果的字幕遮挡条,快捷键V键可以快速启动。增加了隐藏开关和鼠标悬停提示。

// ==UserScript==
// @name         字幕遮挡条
// @namespace    http://tampermonkey.net/
// @version      1.2
// @description  毛玻璃效果的字幕遮挡条,快捷键V键可以快速启动。增加了隐藏开关和鼠标悬停提示。
// @author       Cup Noodle
// @license MIT
// @match        *://*.bilibili.com/*
// @match        *://*.bilibili.t/*
// @grant        GM_addStyle
// @grant        GM_setValue
// @grant        GM_getValue
// ==/UserScript==

(function () {
    'use strict';

    // 初始化样式
    GM_addStyle(`
    #bilibili-overlay-btn {
        position: fixed !important;
        right: 20px !important;
        bottom: 50px !important;
        z-index: 9999 !important;
        padding: 8px 12px !important;
        background-color: rgba(255,255,255,0.9) !important;
        border: 1px solid #ddd !important;
        border-radius: 4px !important;
        cursor: pointer !important;
        font-size: 14px !important;
        transition: opacity 0.3s, visibility 0.3s;
    }
    #toggle-ui-btn {
        position: fixed !important;
        right: 5px !important;
        bottom: 5px !important;
        z-index: 10000 !important;
        width: 15px !important;
        height: 15px !important;
        background-color: rgba(255,255,255,0.8) !important;
        border: 1px solid #ccc !important;
        border-radius: 50% !important;
        cursor: pointer !important;
        font-size: 10px !important;
        line-height: 15px !important;
        text-align: center !important;
        color: #333 !important;
    }
    .hidden-ui {
        opacity: 0 !important;
        visibility: hidden !important;
    }
    `);


    const button = document.createElement('div');
    button.id = 'bilibili-overlay-btn';
    button.textContent = '◼️ 字幕遮挡层';
    // --- 新增代码: 为遮挡层按钮添加悬停提示 ---
    button.title = '按 V 键';
    document.body.appendChild(button);

    const toggleUiBtn = document.createElement('div');
    toggleUiBtn.id = 'toggle-ui-btn';
    toggleUiBtn.textContent = '👁️'; // 初始显示为眼睛图标
    // --- 新增代码: 为UI开关按钮添加悬停提示 ---
    toggleUiBtn.title = '隐藏开关';
    document.body.appendChild(toggleUiBtn);

    // 检查本地存储中UI的可见状态
    let isUiVisible = GM_getValue('isUiVisible', true); // 默认为显示

    function setUiVisibility(visible) {
        if (visible) {
            button.classList.remove('hidden-ui');
            toggleUiBtn.textContent = '👁️';
            toggleUiBtn.title = '隐藏开关'; // 确保提示文本正确
            GM_setValue('isUiVisible', true);
        } else {
            button.classList.add('hidden-ui');
            toggleUiBtn.textContent = '✕';
            toggleUiBtn.title = '显示开关'; // 隐藏后,提示应该变为“显示”
            GM_setValue('isUiVisible', false);
        }
    }

    // 初始化UI状态
    setUiVisibility(isUiVisible);

    // 点击开关按钮,切换UI可见性
    toggleUiBtn.addEventListener('click', () => {
        isUiVisible = !isUiVisible;
        setUiVisibility(isUiVisible);
    });


    const overlay = document.createElement('div');
    overlay.style.position = 'fixed';

    const savedPosition = localStorage.getItem('overlayPosition');
    if (savedPosition) {
        const { top, left, width, height } = JSON.parse(savedPosition);
        overlay.style.top = top;
        overlay.style.left = left;
        overlay.style.width = width;
        overlay.style.height = height;
    } else {
        overlay.style.top = '200px';
        overlay.style.left = '200px';
        overlay.style.width = '900px';
        overlay.style.height = '45px';
    }
    overlay.style.backgroundColor = 'white';
    overlay.style.zIndex = '999999999999';
    overlay.style.cursor = 'default';
    overlay.style.display = 'none';
    overlay.style.borderRadius = '10px';
    overlay.style.background = 'rgba(255,255,255,0.1)';
    overlay.style.backdropFilter = 'blur(10px)';
    overlay.style.webkitBackdropFilter = 'blur(10px)';

    document.body.appendChild(overlay);

    let isDragging = false;
    let dragStartX, dragStartY;

    overlay.addEventListener('mousedown', function (e) {
        const rect = overlay.getBoundingClientRect();
        const isEdge = (
            e.clientX >= rect.right - 10 ||
            e.clientY >= rect.bottom - 10 ||
            e.clientX <= rect.left + 10 ||
            e.clientY <= rect.top + 10
        );

        if (!isEdge) {
            isDragging = true;
            dragStartX = e.clientX - parseInt(overlay.style.left, 10);
            dragStartY = e.clientY - parseInt(overlay.style.top, 10);
            document.body.style.cursor = 'move';
        }
    });

    document.addEventListener('mousemove', function (e) {
        if (isDragging) {
            overlay.style.left = (e.clientX - dragStartX) + 'px';
            overlay.style.top = (e.clientY - dragStartY) + 'px';
        }
    });

    document.addEventListener('mouseup', function () {
        if (isDragging) {
            isDragging = false;
            document.body.style.cursor = 'default';
            localStorage.setItem('overlayPosition', JSON.stringify({
                top: overlay.style.top,
                left: overlay.style.left,
                width: overlay.style.width,
                height: overlay.style.height
            }));
        }
    });

    function addResizableEffect(element) {
        const minWidth = 100;
        const minHeight = 10;

        element.onmousemove = function (e) {
            const rect = element.getBoundingClientRect();
            if (e.clientX > rect.left + rect.width - 10 || rect.left + 10 > e.clientX) {
                element.style.cursor = 'w-resize';
            } else if (e.clientY > rect.top + rect.height - 10) {
                element.style.cursor = 's-resize';
            } else if (e.clientY > rect.top && e.clientY < rect.top + 10) {
                element.style.cursor = 'n-resize';
            } else {
                element.style.cursor = 'default';
            }
        };

        element.onmousedown = (e) => {
            const clientX = e.clientX;
            const clientY = e.clientY;
            const elW = element.clientWidth;
            const elH = element.clientHeight;
            const EloffsetLeft = element.offsetLeft;
            const EloffsetTop = element.offsetTop;
            element.style.userSelect = 'none';

            const isTopResize = clientY > EloffsetTop && clientY < EloffsetTop + 10;

            document.onmousemove = function (e) {
                e.preventDefault();

                if (clientX > EloffsetLeft && clientX < EloffsetLeft + 10) {
                    const newWidth = elW - (e.clientX - clientX);
                    if (newWidth >= minWidth) {
                        element.style.width = newWidth + 'px';
                        element.style.left = EloffsetLeft + (e.clientX - clientX) + 'px';
                    }
                }
                if (clientX > EloffsetLeft + elW - 10 && clientX < EloffsetLeft + elW) {
                    const newWidth = elW + (e.clientX - clientX);
                    if (newWidth >= minWidth) {
                        element.style.width = newWidth + 'px';
                    }
                }
                if (clientY > EloffsetTop + elH - 10 && clientY < EloffsetTop + elH) {
                    const newHeight = elH + (e.clientY - clientY);
                    if (newHeight >= minHeight) {
                        element.style.height = newHeight + 'px';
                    }
                }
                if (isTopResize) {
                    const newHeight = elH - (e.clientY - clientY);
                    if (newHeight >= minHeight) {
                        element.style.height = newHeight + 'px';
                        element.style.top = EloffsetTop + (e.clientY - clientY) + 'px';
                    }
                }
            };

            document.onmouseup = function (e) {
                document.onmousemove = null;
                document.onmouseup = null;
                localStorage.setItem('overlayPosition', JSON.stringify({
                    top: overlay.style.top,
                    left: overlay.style.left,
                    width: overlay.style.width,
                    height: overlay.style.height
                }));
            };
        };
    }

    addResizableEffect(overlay);

    function applyStyles() {
        overlay.style.backdropFilter = `blur(10px)`;
        overlay.style.background = 'rgba(255,255,255,0.1)';
    }

    function toggleOverlay() {
        overlay.style.display = overlay.style.display === 'none' ? '' : 'none';
        button.textContent = overlay.style.display === '' ? '隐藏遮挡层' : '◼️ 字幕遮挡层';
        applyStyles();
    }

    button.addEventListener('click', toggleOverlay);

    document.addEventListener('keydown', function (e) {
        if (e.target.tagName === 'INPUT' || e.target.tagName === 'TEXTAREA' || e.target.isContentEditable) {
            return;
        }
        if (e.key === 'v' || e.key === 'V') {
            toggleOverlay();
        }
    });


    function requestFullscreen(element) {
        if (element.requestFullscreen) {
            element.requestFullscreen();
        } else if (element.webkitRequestFullscreen) {
            element.webkitRequestFullscreen();
        } else if (element.mozRequestFullScreen) {
            element.mozRequestFullScreen();
        } else if (element.msRequestFullscreen) {
            element.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();
        }
    }

    // 监听全屏和网页全屏事件的逻辑保持不变
    // ... (以下代码省略,与之前版本相同) ...
    document.addEventListener('fullscreenchange', function () {
        const container = document.fullscreenElement || document.body;
        if (document.fullscreenElement) {
            button.style.display = 'none';
            toggleUiBtn.style.display = 'none';
            overlay.style.zIndex = '999999999999';
            container.appendChild(overlay);
        } else {
            setUiVisibility(GM_getValue('isUiVisible', true));
            toggleUiBtn.style.display = 'block';
        }
    });
    const observer = new MutationObserver((mutationsList) => {
        for (const mutation of mutationsList) {
            if (mutation.type === 'attributes' && mutation.attributeName === 'class') {
                const videoWrapper = document.querySelector('.bpx-player-container');
                if (videoWrapper && videoWrapper.classList.contains('bpx-player-container-web-fullscreen')) {
                    button.style.display = 'none';
                    toggleUiBtn.style.display = 'none';
                    overlay.style.zIndex = '999999999999';
                } else {
                    setUiVisibility(GM_getValue('isUiVisible', true));
                    toggleUiBtn.style.display = 'block';
                }
            }
        }
    });
    setTimeout(() => {
        const videoWrapper = document.querySelector('.bpx-player-container');
        if (videoWrapper) {
            observer.observe(videoWrapper, { attributes: true, attributeFilter: ['class'] });
        }
    }, 2000);

})();