// ==UserScript==
// @name 图片隐藏
// @namespace http://tampermonkey.net/
// @version 1.2
// @description 单张或者全局图片隐藏与显示
// @match *://*/*
// @author eternal5130
// @license MIT
// @grant GM_setValue
// @grant GM_getValue
// @grant GM_registerMenuCommand
// ==/UserScript==
(function() {
'use strict';
// 默认快捷键配置
const DEFAULT_SHORTCUT = {
single: {
keys: ['MouseMiddle']
},
global: {
keys: ['AltLeft', 'KeyH']
}
};
// 存储图片状态
const imageStates = new Map();
const currentPressedKeys = new Set();
let currentHoverTarget = null;
let configModal = null;
let globalImageCounter = 0;
// 获取快捷键配置
function getShortcutConfig() {
try {
const savedConfig = GM_getValue('imageHiderShortcut');
return savedConfig ? JSON.parse(savedConfig) : { ...DEFAULT_SHORTCUT };
} catch (error) {
console.error('获取快捷键配置失败,使用默认配置', error);
return { ...DEFAULT_SHORTCUT };
}
}
// 保存快捷键配置
function saveShortcutConfig(config) {
try {
GM_setValue('imageHiderShortcut', JSON.stringify(config));
} catch (error) {
console.error('保存快捷键配置失败', error);
alert('保存配置时出错,请重试');
}
}
// 将按键码转换为可读名称
function getKeyDisplayName(code) {
const keyNameMap = {
'AltLeft': 'Alt (左)', 'AltRight': 'Alt (右)',
'ControlLeft': 'Ctrl (左)', 'ControlRight': 'Ctrl (右)',
'ShiftLeft': 'Shift (左)', 'ShiftRight': 'Shift (右)',
'MetaLeft': 'Meta (左)', 'MetaRight': 'Meta (右)',
'MouseLeft': '鼠标左键', 'MouseMiddle': '鼠标中键', 'MouseRight': '鼠标右键',
'KeyA': 'A', 'KeyB': 'B', 'KeyC': 'C', 'KeyD': 'D', 'KeyE': 'E',
'KeyF': 'F', 'KeyG': 'G', 'KeyH': 'H', 'KeyI': 'I', 'KeyJ': 'J',
'KeyK': 'K', 'KeyL': 'L', 'KeyM': 'M', 'KeyN': 'N', 'KeyO': 'O',
'KeyP': 'P', 'KeyQ': 'Q', 'KeyR': 'R', 'KeyS': 'S', 'KeyT': 'T',
'KeyU': 'U', 'KeyV': 'V', 'KeyW': 'W', 'KeyX': 'X', 'KeyY': 'Y', 'KeyZ': 'Z',
'Digit0': '0', 'Digit1': '1', 'Digit2': '2', 'Digit3': '3',
'Digit4': '4', 'Digit5': '5', 'Digit6': '6', 'Digit7': '7',
'Digit8': '8', 'Digit9': '9'
};
return keyNameMap[code] || code;
}
// 检查快捷键是否匹配
function isShortcutMatch(config) {
const { keys = [] } = config;
return keys.every(key => currentPressedKeys.has(key));
}
// 创建模态框样式
function createModalStyles() {
const style = document.createElement('style');
style.textContent = `
.image-hider-modal {
position: fixed;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
background: #fff;
border-radius: 12px;
box-shadow: 0 10px 25px rgba(0,0,0,0.1);
padding: 24px;
z-index: 10000;
width: 500px;
max-height: 80vh;
overflow-y: auto;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif;
position: relative;
}
.image-hider-modal h2 {
color: #333;
margin-bottom: 16px;
font-weight: 600;
}
.image-hider-modal button {
background-color: #4CAF50;
color: white;
border: none;
padding: 10px 16px;
margin: 8px;
border-radius: 6px;
cursor: pointer;
transition: background-color 0.3s;
}
.image-hider-modal button:hover {
background-color: #45a049;
}
.image-hider-modal button.cancel {
background-color: #f44336;
}
.image-hider-modal .current-shortcut {
background-color: #f0f0f0;
padding: 10px;
margin-bottom: 16px;
border-radius: 6px;
text-align: center;
}
.image-hider-modal .shortcut-input {
width: 100%;
padding: 10px;
margin: 10px 0;
border: 1px solid #ddd;
border-radius: 6px;
font-size: 16px;
text-align: center;
}
.image-hider-modal .close-btn {
position: absolute;
right: 16px;
top: 16px;
width: 24px;
height: 24px;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
border-radius: 50%;
background: #f5f5f5;
border: none;
padding: 0;
}
.image-hider-modal .close-btn:hover {
background: #e0e0e0;
}
.image-hider-modal .close-btn::before,
.image-hider-modal .close-btn::after {
content: '';
position: absolute;
width: 14px;
height: 2px;
background: #666;
}
.image-hider-modal .close-btn::before {
transform: rotate(45deg);
}
.image-hider-modal .close-btn::after {
transform: rotate(-45deg);
}
.image-hider-modal .shortcut-section {
background: #f8f9fa;
border-radius: 8px;
padding: 16px;
margin-bottom: 16px;
}
.image-hider-modal .shortcut-section h3 {
color: #2c3e50;
margin: 0 0 12px 0;
font-size: 1.1em;
font-weight: 500;
}
.image-hider-modal .shortcut-section .current-shortcut {
background-color: #fff;
margin-bottom: 12px;
}
.image-hider-modal .shortcut-section button {
margin: 8px 0;
width: 100%;
}
.image-hider-modal .button-group {
margin-top: 24px;
}
.settings-item {
display: flex;
align-items: center;
margin: 16px 0;
padding: 8px;
background: #fff;
border-radius: 8px;
}
.settings-label {
margin-left: 12px;
color: #495057;
}
.image-hider-switch {
position: relative;
display: inline-block;
width: 60px;
height: 34px;
}
.image-hider-switch input {
opacity: 0;
width: 0;
height: 0;
}
.image-hider-slider {
position: absolute;
cursor: pointer;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: #ccc;
transition: background-color 0.4s, transform 0.4s;
border-radius: 34px;
box-shadow: inset 0 0 5px rgba(0, 0, 0, 0.2);
}
.image-hider-slider:before {
position: absolute;
content: "";
height: 26px;
width: 26px;
left: 4px;
bottom: 4px;
background-color: white;
transition: transform 0.4s;
border-radius: 50%;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
}
.image-hider-switch input:checked + .image-hider-slider {
background-color: #4CAF50;
}
.image-hider-switch input:checked + .image-hider-slider:before {
transform: translateX(26px);
}
.image-hider-slider:hover {
background-color: #b3b3b3;
}
.settings-item {
display: flex;
align-items: center;
justify-content: space-between;
padding: 16px 20px;
transition: background-color 0.2s ease;
}
.settings-content {
flex: 1;
margin-right: 16px;
}
.settings-title {
font-size: 14px;
font-weight: 500;
color: #303133;
margin-bottom: 4px;
}
.settings-description {
font-size: 12px;
color: #909399;
line-height: 1.4;
}
`;
document.head.appendChild(style);
}
// 添加全局状态存储
function getGlobalHideState() {
return GM_getValue('globalHideState', false);
}
function setGlobalHideState(state) {
GM_setValue('globalHideState', state);
}
// 修改 toggleAllImages 函数
function toggleAllImages() {
const images = document.getElementsByTagName('img');
Array.from(images).forEach(img => {
if (!img.dataset.imageId) {
img.dataset.imageId = `img_${++globalImageCounter}`;
imageStates.set(img.dataset.imageId, new ImageState(img));
}
});
const visibleImages = Array.from(images).filter(img => {
const state = imageStates.get(img.dataset.imageId);
return !state?.isHidden;
});
const shouldHide = visibleImages.length > 0;
// 更新全局状态
setGlobalHideState(shouldHide);
requestAnimationFrame(() => {
Array.from(images).forEach(img => {
toggleImageVisibility(img, shouldHide, false);
});
});
}
// 修改初始化函数
function initAutoHide() {
const shouldHide = getGlobalHideState();
if (shouldHide) {
// 立即处理当前可见的图片
const processImages = () => {
const images = document.getElementsByTagName('img');
Array.from(images).forEach(img => {
if (!img.dataset.imageId) {
img.dataset.imageId = `img_${++globalImageCounter}`;
imageStates.set(img.dataset.imageId, new ImageState(img));
}
// 立即设置初始样式,防止图片闪现
img.style.cssText = `
opacity: 0 !important;
pointer-events: none !important;
transition: none !important;
`;
if (img.complete) {
toggleImageVisibility(img, true, true);
} else {
// 在图片加载前就开始监听
img.addEventListener('load', () => toggleImageVisibility(img, true, true), { once: true });
}
});
};
// 首次处理
processImages();
// 使用 MutationObserver 监听 DOM 变化
const observer = new MutationObserver((mutations) => {
mutations.forEach(mutation => {
mutation.addedNodes.forEach(node => {
if (node.tagName === 'IMG') {
// 立即设置初始样式
node.style.cssText = `
opacity: 0 !important;
pointer-events: none !important;
transition: none !important;
`;
toggleImageVisibility(node, true, true);
} else if (node.getElementsByTagName) {
const images = node.getElementsByTagName('img');
Array.from(images).forEach(img => {
// 立即设置初始样式
img.style.cssText = `
opacity: 0 !important;
pointer-events: none !important;
transition: none !important;
`;
toggleImageVisibility(img, true, true);
});
}
});
});
});
observer.observe(document.body, {
childList: true,
subtree: true,
attributes: true,
attributeFilter: ['src']
});
// 添加 DOM 内容加载完成的处理
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', processImages);
}
// 添加页面完全加载完成的处理
window.addEventListener('load', processImages);
// 清理函数
const cleanup = () => {
observer.disconnect();
};
// 页面卸载时清理
window.addEventListener('unload', cleanup);
}
}
// 修改配置弹窗,移除自动隐藏开关相关代码
function createConfigModal() {
if (!document.querySelector('#image-hider-modal-styles')) {
const style = document.createElement('style');
style.id = 'image-hider-modal-styles';
style.textContent = `
.image-hider-modal {
position: fixed;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
background: #fff;
border-radius: 12px;
box-shadow: 0 10px 25px rgba(0,0,0,0.15);
padding: 28px;
z-index: 10000;
width: 480px;
max-height: 80vh;
overflow-y: auto;
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
animation: modalFadeIn 0.2s ease-out;
box-sizing: border-box;
}
.image-hider-modal * {
box-sizing: border-box;
}
@keyframes modalFadeIn {
from {
opacity: 0;
transform: translate(-50%, -48%);
}
to {
opacity: 1;
transform: translate(-50%, -50%);
}
}
.image-hider-modal h2 {
color: #2c3e50;
margin: 0 0 20px 0;
font-size: 1.5em;
font-weight: 600;
line-height: 1.4;
}
.image-hider-modal .shortcut-section {
background: #f8f9fa;
border-radius: 8px;
padding: 20px;
margin-bottom: 20px;
border: 1px solid #e9ecef;
}
.image-hider-modal .shortcut-section h3 {
color: #2c3e50;
margin: 0 0 16px 0;
font-size: 1.1em;
font-weight: 500;
line-height: 1.4;
}
.image-hider-modal .current-shortcut {
background-color: #fff;
padding: 12px 16px;
margin-bottom: 16px;
border-radius: 8px;
text-align: center;
border: 1px solid #e9ecef;
color: #495057;
line-height: 1.5;
font-size: 14px;
}
.image-hider-modal .shortcut-input {
width: 100%;
height: 42px;
padding: 8px 16px;
margin: 10px 0;
border: 2px solid #e9ecef;
border-radius: 8px;
font-size: 14px;
text-align: center;
background: #fff;
transition: all 0.2s ease;
color: #495057;
line-height: 1.5;
display: block;
}
.image-hider-modal .shortcut-input:focus {
border-color: #4CAF50;
outline: none;
box-shadow: 0 0 0 3px rgba(76, 175, 80, 0.1);
}
.image-hider-modal .shortcut-input.recording {
background: #e8f5e9;
border-color: #4CAF50;
}
.image-hider-modal .shortcut-input::placeholder {
color: #adb5bd;
}
.image-hider-modal .button-wrapper {
display: flex;
justify-content: center;
margin: 12px 0;
}
.image-hider-modal button {
padding: 10px 20px;
border-radius: 8px;
border: none;
cursor: pointer;
font-size: 14px;
font-weight: 500;
transition: all 0.2s ease;
min-width: 120px;
height: 40px;
line-height: 20px;
display: inline-flex;
align-items: center;
justify-content: center;
margin: 0;
}
.image-hider-modal button#setShortcutBtn,
.image-hider-modal button#setSingleShortcutBtn,
.image-hider-modal button#setGlobalShortcutBtn {
background-color: #f8f9fa;
color: #495057;
border: 1px solid #dee2e6;
width: 100%;
}
.image-hider-modal button#setShortcutBtn:hover,
.image-hider-modal button#setSingleShortcutBtn:hover,
.image-hider-modal button#setGlobalShortcutBtn:hover {
background-color: #e9ecef;
}
.image-hider-modal button#setShortcutBtn.recording,
.image-hider-modal button#setSingleShortcutBtn.recording,
.image-hider-modal button#setGlobalShortcutBtn.recording {
background-color: #4CAF50;
color: white;
border: none;
}
.image-hider-modal .button-group {
display: flex;
gap: 12px;
margin-top: 24px;
justify-content: center;
}
.image-hider-modal button#saveConfigBtn {
background-color: #4CAF50;
color: white;
min-width: 140px;
}
.image-hider-modal button#saveConfigBtn:hover {
background-color: #43a047;
}
.image-hider-modal .close-btn {
position: absolute;
right: 16px;
top: 16px;
width: 32px;
height: 32px;
border-radius: 16px;
background: #f8f9fa;
border: none;
cursor: pointer;
transition: all 0.2s ease;
padding: 0;
min-width: unset;
display: flex;
align-items: center;
justify-content: center;
}
.image-hider-modal .close-btn:hover {
background: #e9ecef;
}
.image-hider-modal .close-btn::before,
.image-hider-modal .close-btn::after {
content: '';
position: absolute;
width: 16px;
height: 2px;
background: #495057;
transition: background 0.2s;
}
.image-hider-modal .close-btn::before {
transform: rotate(45deg);
}
.image-hider-modal .close-btn::after {
transform: rotate(-45deg);
}
.image-hider-modal .hint {
color: #6c757d;
font-size: 13px;
margin-top: 20px;
line-height: 1.6;
padding: 16px;
background: #f8f9fa;
border-radius: 8px;
border: 1px solid #e9ecef;
}
.image-hider-modal .success-message {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
background: rgba(76, 175, 80, 0.9);
color: white;
padding: 16px 32px;
border-radius: 8px;
font-weight: 500;
opacity: 0;
transition: opacity 0.3s ease;
pointer-events: none;
}
.image-hider-modal .success-message.show {
opacity: 1;
}
.image-hider-modal.closing {
opacity: 0;
transform: translate(-50%, -48%);
transition: all 0.3s ease;
}
`;
document.head.appendChild(style);
}
if (configModal) {
configModal.remove();
}
configModal = document.createElement('div');
configModal.className = 'image-hider-modal';
const shortcutConfig = getShortcutConfig();
const singleShortcutDisplay = (shortcutConfig.single.keys || DEFAULT_SHORTCUT.single.keys)
.map(key => getKeyDisplayName(key))
.join(' + ');
const globalShortcutDisplay = (shortcutConfig.global.keys || DEFAULT_SHORTCUT.global.keys)
.map(key => getKeyDisplayName(key))
.join(' + ');
configModal.innerHTML = `
<button class="close-btn" id="closeConfigBtn"></button>
<h2>图片隐藏快捷键配置</h2>
<div class="shortcut-section">
<h3>单个图片快捷键</h3>
<div class="current-shortcut">
<strong>当前快捷键:</strong>${singleShortcutDisplay}
</div>
<input type="text" readonly class="shortcut-input" id="singleShortcutInput"
placeholder="点击"设置快捷键"开始录制">
<div class="button-wrapper">
<button id="setSingleShortcutBtn">设置快捷键</button>
</div>
</div>
<div class="shortcut-section">
<h3>全局图片快捷键</h3>
<div class="current-shortcut">
<strong>当前快捷键:</strong>${globalShortcutDisplay}
</div>
<input type="text" readonly class="shortcut-input" id="globalShortcutInput"
placeholder="点击"设置快捷键"开始录制">
<div class="button-wrapper">
<button id="setGlobalShortcutBtn">设置快捷键</button>
</div>
</div>
<div class="button-group">
<button id="saveConfigBtn">保存设置</button>
</div>
<div class="hint">
<strong>使用说明:</strong><br>
1. 单个图片快捷键:悬停在图片上时触发隐藏/显示<br>
2. 全局图片快捷键:一键切换所有图片的显示状态,状态会在新页面保持<br>
3. 点击对应的"设置快捷键"开始录制<br>
4. 完成设置后点击"保存设置"确认更改
</div>
`;
document.body.appendChild(configModal);
const setSingleShortcutBtn = configModal.querySelector('#setSingleShortcutBtn');
const setGlobalShortcutBtn = configModal.querySelector('#setGlobalShortcutBtn');
const saveConfigBtn = configModal.querySelector('#saveConfigBtn');
const closeConfigBtn = configModal.querySelector('#closeConfigBtn');
const singleShortcutInput = configModal.querySelector('#singleShortcutInput');
const globalShortcutInput = configModal.querySelector('#globalShortcutInput');
let isRecording = false;
let currentRecordingType = null;
let singlePressedKeys = new Set();
let globalPressedKeys = new Set();
function startRecording(type, input, button) {
if (isRecording) return;
isRecording = true;
currentRecordingType = type;
const keysSet = type === 'single' ? singlePressedKeys : globalPressedKeys;
keysSet.clear();
input.value = '请按下快捷键组合...';
input.classList.add('recording');
button.classList.add('recording');
button.textContent = '正在录制...';
const keydownHandler = (e) => {
e.preventDefault();
keysSet.add(e.code);
updateShortcutDisplay(input, keysSet);
};
const keyupHandler = (e) => {
e.preventDefault();
if (keysSet.size > 0) {
finishRecording(input, button);
}
};
const mousedownHandler = (e) => {
e.preventDefault();
if (e.button === 1) {
keysSet.add('MouseMiddle');
updateShortcutDisplay(input, keysSet);
}
};
const mouseupHandler = (e) => {
e.preventDefault();
if (e.button === 1 && keysSet.size > 0) {
finishRecording(input, button);
}
};
document.addEventListener('keydown', keydownHandler);
document.addEventListener('keyup', keyupHandler);
document.addEventListener('mousedown', mousedownHandler);
document.addEventListener('mouseup', mouseupHandler);
// 保存清理函数
button.cleanup = () => {
document.removeEventListener('keydown', keydownHandler);
document.removeEventListener('keyup', keyupHandler);
document.removeEventListener('mousedown', mousedownHandler);
document.removeEventListener('mouseup', mouseupHandler);
};
}
function updateShortcutDisplay(input, keysSet) {
input.value = Array.from(keysSet)
.map(getKeyDisplayName)
.join(' + ');
}
function finishRecording(input, button) {
isRecording = false;
currentRecordingType = null;
input.classList.remove('recording');
button.classList.remove('recording');
button.textContent = '设置快捷键';
if (button.cleanup) {
button.cleanup();
button.cleanup = null;
}
}
setSingleShortcutBtn.addEventListener('click', () => {
startRecording('single', singleShortcutInput, setSingleShortcutBtn);
});
setGlobalShortcutBtn.addEventListener('click', () => {
startRecording('global', globalShortcutInput, setGlobalShortcutBtn);
});
saveConfigBtn.addEventListener('click', () => {
const shortcutConfig = getShortcutConfig();
const settings = getSettings();
let hasChanges = false;
// 检查并保存单个图片快捷键
if (singleShortcutInput.value && singleShortcutInput.value !== '请按下快捷键组合...') {
shortcutConfig.single.keys = Array.from(singlePressedKeys);
hasChanges = true;
}
// 检查并保存全局快捷键
if (globalShortcutInput.value && globalShortcutInput.value !== '请按下快捷键组合...') {
shortcutConfig.global.keys = Array.from(globalPressedKeys);
hasChanges = true;
}
if (hasChanges) {
saveShortcutConfig(shortcutConfig);
// 创建成功提示
const successMessage = document.createElement('div');
successMessage.className = 'success-message';
successMessage.textContent = '快捷键设置已保存';
configModal.appendChild(successMessage);
// 添加过渡效果
requestAnimationFrame(() => {
successMessage.classList.add('show');
setTimeout(() => {
configModal.classList.add('closing');
setTimeout(() => {
configModal.remove();
configModal = null;
}, 300);
}, 500);
});
} else {
alert('请先设置至少一个快捷键');
}
});
closeConfigBtn.addEventListener('click', () => {
if (isRecording) {
const button = currentRecordingType === 'single' ? setSingleShortcutBtn : setGlobalShortcutBtn;
const input = currentRecordingType === 'single' ? singleShortcutInput : globalShortcutInput;
finishRecording(input, button);
}
configModal.remove();
configModal = null;
});
// 添加点击外部关闭功能
document.addEventListener('mousedown', (e) => {
if (configModal && !configModal.contains(e.target)) {
closeConfigBtn.click();
}
});
}
// 添加用户操作标记
function ImageState(imgElement) {
this.element = imgElement;
this.originalOpacity = getComputedStyle(imgElement).opacity;
this.originalPointerEvents = getComputedStyle(imgElement).pointerEvents;
this.isHidden = false;
this.overlay = null;
this.observers = new Set();
this.lastToggleTime = 0;
this.userManuallyShown = false;
this.toggleDebounceDelay = 300;
this.isProcessing = false;
}
ImageState.prototype.cleanup = function() {
this.observers.forEach(observer => observer.disconnect());
this.observers.clear();
if (this.overlay) {
this.overlay.cleanup?.();
this.overlay.remove();
this.overlay = null;
}
};
// 优化覆盖层创建
function createOverlay(imgElement) {
if (!imgElement || !imgElement.parentNode) return null;
const overlay = document.createElement('div');
const state = imageStates.get(imgElement.dataset.imageId);
if (!state) return null;
const updatePosition = throttle(() => {
if (!imgElement.parentNode) {
overlay.remove();
return;
}
const rect = imgElement.getBoundingClientRect();
if (rect.width === 0 || rect.height === 0) return;
Object.assign(overlay.style, {
position: 'absolute',
left: `${rect.left + window.scrollX}px`,
top: `${rect.top + window.scrollY}px`,
width: `${rect.width}px`,
height: `${rect.height}px`,
backgroundColor: 'transparent',
pointerEvents: 'auto',
zIndex: '9999'
});
}, 16);
updatePosition();
overlay.dataset.linkedImage = imgElement.dataset.imageId;
const intersectionObserver = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
updatePosition();
}
});
}, {
threshold: [0, 1]
});
intersectionObserver.observe(imgElement);
state.observers.add(intersectionObserver);
const resizeObserver = new ResizeObserver(updatePosition);
resizeObserver.observe(imgElement);
state.observers.add(resizeObserver);
const scrollHandler = () => requestAnimationFrame(updatePosition);
window.addEventListener('scroll', scrollHandler, { passive: true });
overlay.addEventListener('mouseover', () => currentHoverTarget = imgElement);
overlay.addEventListener('mouseout', () => currentHoverTarget = null);
overlay.cleanup = () => {
window.removeEventListener('scroll', scrollHandler);
intersectionObserver.disconnect();
resizeObserver.disconnect();
};
document.body.appendChild(overlay);
return overlay;
}
// 修改 toggleImageVisibility 函数
function toggleImageVisibility(imgElement, forceHide = false, isAutoHide = false) {
if (!imgElement || !imgElement.parentNode) return;
if (!imgElement.dataset.imageId) {
imgElement.dataset.imageId = `img_${++globalImageCounter}`;
imageStates.set(imgElement.dataset.imageId, new ImageState(imgElement));
}
const state = imageStates.get(imgElement.dataset.imageId);
if (!state) return;
const currentTime = Date.now();
if (currentTime - state.lastToggleTime < state.toggleDebounceDelay || state.isProcessing) {
return;
}
state.isProcessing = true;
state.lastToggleTime = currentTime;
if (isAutoHide && state.userManuallyShown) {
state.isProcessing = false;
return;
}
if (!isAutoHide) {
state.userManuallyShown = !state.isHidden;
}
const shouldHide = forceHide || !state.isHidden;
try {
requestAnimationFrame(() => {
if (shouldHide) {
if (!state.overlay) {
state.overlay = createOverlay(imgElement);
}
imgElement.style.cssText = `
opacity: 0 !important;
pointer-events: none !important;
transition: opacity 0.2s ease !important;
`;
} else {
imgElement.style.cssText = `
opacity: ${state.originalOpacity || '1'} !important;
pointer-events: ${state.originalPointerEvents || 'auto'} !important;
transition: opacity 0.2s ease !important;
`;
state.cleanup();
}
state.isHidden = shouldHide;
setTimeout(() => {
state.isProcessing = false;
if (!shouldHide && parseFloat(getComputedStyle(imgElement).opacity) === 0) {
imgElement.style.cssText = `
opacity: ${state.originalOpacity || '1'} !important;
pointer-events: ${state.originalPointerEvents || 'auto'} !important;
`;
}
}, 250);
});
} catch (error) {
console.error('Toggle image visibility error:', error);
state.isProcessing = false;
}
}
// 添加节流函数
function throttle(func, limit) {
let inThrottle;
return function(...args) {
if (!inThrottle) {
func.apply(this, args);
inThrottle = true;
setTimeout(() => inThrottle = false, limit);
}
}
}
// 键盘按下事件
function handleKeyDown(event) {
currentPressedKeys.add(event.code);
checkShortcut();
}
// 键盘抬起事件
function handleKeyUp(event) {
currentPressedKeys.delete(event.code);
}
// 鼠标悬停处理
function handleMouseOver(event) {
if (event.target.tagName.toLowerCase() === 'img') {
currentHoverTarget = event.target;
}
}
// 鼠标移出处理
function handleMouseOut(event) {
if (event.target.tagName.toLowerCase() === 'img') {
currentHoverTarget = null;
}
}
// 重置状态的函数
function resetState() {
currentPressedKeys.clear();
currentHoverTarget = null;
}
// 注册事件监听
document.addEventListener('mouseover', handleMouseOver);
document.addEventListener('mouseout', handleMouseOut);
document.addEventListener('keydown', handleKeyDown);
document.addEventListener('keyup', handleKeyUp);
// 页面刷新/切换时重置按键状态
window.addEventListener('beforeunload', resetState);
// 注册菜单命令
GM_registerMenuCommand('配置快捷键', createConfigModal);
// 增加鼠标事件处理函数
function handleMouseDown(event) {
if (event.button === 1) {
event.preventDefault();
currentPressedKeys.add('MouseMiddle');
checkShortcut();
}
}
function handleMouseUp(event) {
if (event.button === 1) {
currentPressedKeys.delete('MouseMiddle');
}
}
// 检查快捷键组合是否匹配
function checkShortcut() {
const shortcutConfig = getShortcutConfig();
// 检查单个图片快捷键
if (isShortcutMatch(shortcutConfig.single) &&
currentHoverTarget &&
currentHoverTarget.tagName.toLowerCase() === 'img') {
toggleImageVisibility(currentHoverTarget);
}
// 检查全局快捷键
if (isShortcutMatch(shortcutConfig.global)) {
toggleAllImages();
}
}
// 在主函数中添加鼠标事件监听
document.addEventListener('mousedown', handleMouseDown);
document.addEventListener('mouseup', handleMouseUp);
// 修改页面加载事件监听
document.addEventListener('DOMContentLoaded', initAutoHide);
if (document.readyState !== 'loading') {
initAutoHide();
}
})();