// ==UserScript== // @name 视频字幕遮罩条 // @namespace http://tampermonkey.net/ // @version 1.3 // @description 创建一个磨砂半透明遮罩条来遮挡视频字幕 // @author Aaron Hu // @match *://*/* // @grant GM_getValue // @grant GM_setValue // @grant GM_addStyle // @grant GM_registerMenuCommand // @license MIT // @downloadURL none // ==/UserScript== (function() { 'use strict'; // 检查是否已经存在遮罩条 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); } } })();