Greasy Fork

Greasy Fork is available in English.

视频字幕遮罩条

创建一个磨砂半透明遮罩条来遮挡视频字幕

当前为 2024-12-03 提交的版本,查看 最新版本

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Greasemonkey 油猴子Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Violentmonkey 暴力猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴Userscripts ,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey 篡改猴,才能安装此脚本。

您需要先安装一款用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name         视频字幕遮罩条
// @namespace    http://tampermonkey.net/
// @version      1.4
// @description  创建一个磨砂半透明遮罩条来遮挡视频字幕
// @author       Aaron Hu
// @match        *://*/*
// @grant        GM_getValue
// @grant        GM_setValue
// @grant        GM_addStyle
// @grant        GM_registerMenuCommand
// @license      MIT
// ==/UserScript==

(function() {
    'use strict';

    // 检查页面是否包含视频元素
    const hasVideo = document.querySelector('video');
    if (!hasVideo) {
        console.log('页面中没有视频元素');
        return;
    }

    // 检查是否已经存在遮罩条
    if (document.querySelector('#subtitle-mask-container')) {
        console.log('遮罩条已存在');
        return;
    }

    // 创建容器和遮罩条
    const container = document.createElement('div');
    container.id = 'subtitle-mask-container'; // 为容器添加唯一ID
    const mask = document.createElement('div');

    // 创建关闭按钮
    const closeButton = document.createElement('div');
    closeButton.innerHTML = '×'; // 使用乘号符号作为关闭按钮
    Object.assign(closeButton.style, {
        position: 'absolute',
        top: '5px',
        right: '5px',
        width: '20px',
        height: '20px',
        backgroundColor: 'rgba(255, 255, 255, 0.7)',
        color: 'black',
        textAlign: 'center',
        lineHeight: '20px',
        borderRadius: '50%',
        cursor: 'pointer',
        zIndex: '2147483648', // 确保在遮罩条之上
    });

    // 添加关闭按钮的点击事件
    closeButton.addEventListener('click', () => {
        container.remove();
        GM_setValue('maskVisible', false); // 更新存储状态
    });

    // 将关闭按钮添加到遮罩条
    mask.appendChild(closeButton);

    // 从存储中获取显示状态和位置信息
    let isVisible = GM_getValue('maskVisible', false);
    let maskConfig = GM_getValue('maskConfig', {
        width: '80%',
        height: '80px',
        bottom: '30px',
        xOffset: 0
    });

    // 设置容器样式
    Object.assign(container.style, {
        position: 'fixed',
        top: '0',
        left: '0',
        width: '100%',
        height: '100%',
        zIndex: '2147483647',
        pointerEvents: 'none',
        display: isVisible ? 'block' : 'none'
    });

    // 设置遮罩条的样式
    Object.assign(mask.style, {
        position: 'fixed',
        bottom: maskConfig.bottom,
        left: '50%',
        transform: `translateX(calc(-50% + ${maskConfig.xOffset}px))`,
        width: maskConfig.width,
        height: maskConfig.height,
        backgroundColor: 'rgba(255, 255, 255, 0.2)',
        backdropFilter: 'blur(8px)',
        zIndex: '2147483647',
        cursor: 'move',
        border: '1px solid rgba(255, 255, 255, 0.1)',
        borderRadius: '4px',
        pointerEvents: 'auto',
        userSelect: 'none',
    });

    // 添加切换显示状态的函数
    function toggleMask() {
        isVisible = !isVisible;
        GM_setValue('maskVisible', isVisible);
        container.style.display = isVisible ? 'block' : 'none';
    }

    // 注册油猴菜单命令
    GM_registerMenuCommand('切换遮罩条显示', toggleMask);

    // 监听油猴脚本的启用/禁用状态
    const observer = new MutationObserver((mutations) => {
        mutations.forEach((mutation) => {
            if (mutation.attributeName === 'disabled') {
                const scriptTag = document.querySelector('script[src*="subtitle-mask.user.js"]');
                if (scriptTag) {
                    const isEnabled = !scriptTag.hasAttribute('disabled');
                    container.style.display = isEnabled && isVisible ? 'block' : 'none';
                }
            }
        });
    });

    // 观察脚本标签的变化
    const scriptTag = document.querySelector('script[src*="subtitle-mask.user.js"]');
    if (scriptTag) {
        observer.observe(scriptTag, {
            attributes: true,
            attributeFilter: ['disabled']
        });
    }

    // 修改拖动功能的代码部分
    let isDragging = false;
    let currentX, currentY;
    let initialX, initialY;
    let xOffset = 0, yOffset = 0;

    mask.addEventListener('mousedown', dragStart);
    document.addEventListener('mousemove', drag);
    document.addEventListener('mouseup', dragEnd);

    function dragStart(e) {
        // 记录鼠标按下时的初始位置
        const rect = mask.getBoundingClientRect();
        xOffset = rect.left + rect.width / 2;  // 考虑元素的中心点
        yOffset = window.innerHeight - rect.top - rect.height / 2;  // 从底部计算偏移

        initialX = e.clientX - (rect.left + rect.width / 2);
        initialY = e.clientY - (rect.top + rect.height / 2);

        isDragging = true;
    }

    function drag(e) {
        if (!isDragging) return;
        e.preventDefault();

        // 计算新位置
        const x = e.clientX - initialX - window.innerWidth / 2;
        const y = window.innerHeight - (e.clientY - initialY) - mask.offsetHeight / 2;

        // 更新位置
        mask.style.left = '50%';
        mask.style.bottom = `${y}px`;
        mask.style.transform = `translate(calc(-50% + ${x}px), 0)`;

        // 保存位置信息
        maskConfig.bottom = `${y}px`;
        maskConfig.xOffset = x;
        GM_setValue('maskConfig', maskConfig);
    }

    function dragEnd() {
        isDragging = false;
    }

    // 添加左右两侧的宽度调节器
    const leftResizer = document.createElement('div');
    const rightResizer = document.createElement('div');

    // 设置调节器的基础样式
    const resizerStyles = {
        position: 'absolute',
        top: '0',
        width: '10px',
        height: '100%',
        backgroundColor: 'rgba(255, 255, 255, 0.3)',
        cursor: 'ew-resize',
        borderRadius: '4px',
    };

    // 设置左侧调节器
    Object.assign(leftResizer.style, {
        ...resizerStyles,
        left: '-5px',
    });

    // 设置右侧调节器
    Object.assign(rightResizer.style, {
        ...resizerStyles,
        right: '-5px',
    });

    // 宽度调节相关变量
    let isResizingWidth = false;
    let resizingSide = null;
    let initialWidth;
    let initialMouseX;

    // 开始调节宽度
    function widthResizeStart(e, side) {
        isResizingWidth = true;
        resizingSide = side;
        initialWidth = mask.offsetWidth;
        initialMouseX = e.clientX;
        e.stopPropagation();
    }

    // 调节宽度过程
    function widthResize(e) {
        if (!isResizingWidth) return;
        e.preventDefault();

        const deltaX = e.clientX - initialMouseX;
        const newWidth = resizingSide === 'right'
            ? initialWidth + deltaX
            : initialWidth - deltaX;

        // 设置最小宽度限制为100px
        if (newWidth > 100) {
            mask.style.width = `${newWidth}px`;
            if (resizingSide === 'left') {
                // 左侧调节时保持中心点不变
                const xAdjustment = deltaX / 2;
                mask.style.transform = `translateX(calc(-50% + ${xAdjustment}px))`;
                maskConfig.xOffset = xAdjustment;
            }
            maskConfig.width = `${newWidth}px`;
            GM_setValue('maskConfig', maskConfig);
        }
    }

    // 结束调节宽度
    function widthResizeEnd() {
        isResizingWidth = false;
        resizingSide = null;
    }

    // 添加事件监听
    leftResizer.addEventListener('mousedown', (e) => widthResizeStart(e, 'left'));
    rightResizer.addEventListener('mousedown', (e) => widthResizeStart(e, 'right'));
    document.addEventListener('mousemove', widthResize);
    document.addEventListener('mouseup', widthResizeEnd);

    // 将调节器添加到遮罩条
    mask.appendChild(leftResizer);
    mask.appendChild(rightResizer);

    // 在宽度调节器代码后添加高度调节器代码
    const heightResizer = document.createElement('div');

    // 设置高度调节器的样式
    Object.assign(heightResizer.style, {
        position: 'absolute',
        top: '-5px',
        left: '50%',
        transform: 'translateX(-50%)',
        width: '40px',
        height: '10px',
        backgroundColor: 'rgba(255, 255, 255, 0.3)',
        cursor: 'ns-resize',
        borderRadius: '4px',
    });

    // 高度调节相关变量
    let isResizingHeight = false;
    let initialHeight;
    let initialMouseY;

    // 开始调节高度
    function heightResizeStart(e) {
        isResizingHeight = true;
        initialHeight = mask.offsetHeight;
        initialMouseY = e.clientY;
        e.stopPropagation();
    }

    // 调节高度过程
    function heightResize(e) {
        if (!isResizingHeight) return;
        e.preventDefault();

        const deltaY = initialMouseY - e.clientY;
        const newHeight = initialHeight + deltaY;

        // 设置最小高度限制为20px
        if (newHeight > 20) {
            mask.style.height = `${newHeight}px`;
            maskConfig.height = `${newHeight}px`;
            GM_setValue('maskConfig', maskConfig);
        }
    }

    // 结束调节高度
    function heightResizeEnd() {
        isResizingHeight = false;
    }

    // 添加高度调节相关的事件监听
    heightResizer.addEventListener('mousedown', heightResizeStart);
    document.addEventListener('mousemove', heightResize);
    document.addEventListener('mouseup', heightResizeEnd);

    // 将高度调节器添加到遮罩条
    mask.appendChild(heightResizer);

    // 确保遮罩条立即显示
    container.style.display = isVisible ? 'block' : 'none';
    container.appendChild(mask);
    document.body.appendChild(container);

    // 添加调试信息
    console.log('遮罩条已创建', {
        isVisible,
        containerDisplay: container.style.display,
        maskStyles: mask.style,
        containerStyles: container.style
    });

    // 添加全屏检测和处理
    document.addEventListener('fullscreenchange', handleFullscreen);
    document.addEventListener('webkitfullscreenchange', handleFullscreen);
    document.addEventListener('mozfullscreenchange', handleFullscreen);
    document.addEventListener('MSFullscreenChange', handleFullscreen);

    function handleFullscreen() {
        const fullscreenElement = document.fullscreenElement || 
                                 document.webkitFullscreenElement || 
                                 document.mozFullScreenElement || 
                                 document.msFullscreenElement;
        
        if (fullscreenElement) {
            // 在全屏元素内重新添加遮罩
            fullscreenElement.appendChild(container);
        } else {
            // 退出全屏时重新添加到 body
            document.body.appendChild(container);
        }
    }
})();