Native Image Toggler

通过原生 UI 控制当前页面图片的显示和隐藏,支持持久化设置

이 스크립트를 설치하려면 Tampermonkey, Greasemonkey 또는 Violentmonkey와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 Tampermonkey와 같은 확장 프로그램을 설치해야 합니다.

이 스크립트를 설치하려면 Tampermonkey 또는 Violentmonkey와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 Tampermonkey 또는 Userscripts와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 Tampermonkey와 같은 확장 프로그램이 필요합니다.

이 스크립트를 설치하려면 유저 스크립트 관리자 확장 프로그램이 필요합니다.

(이미 유저 스크립트 관리자가 설치되어 있습니다. 설치를 진행합니다!)

이 스타일을 설치하려면 Stylus와 같은 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 Stylus와 같은 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 Stylus와 같은 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 유저 스타일 관리자 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 유저 스타일 관리자 확장 프로그램이 필요합니다.

이 스타일을 설치하려면 유저 스타일 관리자 확장 프로그램이 필요합니다.

(이미 유저 스타일 관리자가 설치되어 있습니다. 설치를 진행합니다!)

// ==UserScript==
// @name         Native Image Toggler
// @namespace    http://tampermonkey.net/
// @version      0.3
// @description  通过原生 UI 控制当前页面图片的显示和隐藏,支持持久化设置
// @author       You
// @match        *://*/*
// @grant        GM_setValue
// @grant        GM_getValue
// @grant        GM_registerMenuCommand
// @grant        GM_addStyle
// @run-at       document-start
// ==/UserScript==

(function() {
    'use strict';

    // 配置常量
    const STORAGE_KEY_PREFIX = 'img_toggle_';
    const STYLE_ID = 'native-image-toggler-style';
    const POSITION_KEY = 'img_toggle_position_';
    const UI_VISIBLE_KEY = 'img_toggle_ui_visible_';
    const DEFAULT_POSITION = { top: 10, right: 20 };

    // 获取当前域名的设置键
    const hostname = window.location.hostname;
    const storageKey = STORAGE_KEY_PREFIX + hostname;
    const positionKey = POSITION_KEY + hostname;

    // 状态:true 表示图片显示(默认),false 表示图片隐藏
    // 默认所有网站都是显示的,除非用户手动关闭
    let isImagesVisible = GM_getValue(storageKey, true);
    let uiPosition = GM_getValue(positionKey, DEFAULT_POSITION);
    let isUIVisible = GM_getValue(UI_VISIBLE_KEY, true);

    // CSS 样式:隐藏图片的样式
    const hideImageCSS = `
        img, image, picture, svg, canvas, video, iframe {
            opacity: 0 !important;
            visibility: hidden !important;
            pointer-events: none !important;
        }
        * {
            background-image: none !important;
        }
        /* 排除我们自己的 UI */
        #image-toggler-ui, #image-toggler-ui * {
            opacity: 1 !important;
            visibility: visible !important;
            pointer-events: auto !important;
            background-image: initial !important;
        }
    `;

    // CSS 样式:UI 控件的样式
    const uiCSS = `
        #image-toggler-ui {
            position: fixed;
            z-index: 2147483647; /* Max Z-Index */
            font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
            background-color: rgba(30, 30, 30, 0.8);
            backdrop-filter: blur(10px);
            color: #fff;
            padding: 8px;
            border-radius: 16px;
            cursor: grab;
            user-select: none;
            box-shadow: 0 4px 15px rgba(0,0,0,0.3);
            transition: transform 0.2s ease, box-shadow 0.2s ease;
            font-size: 14px;
            font-weight: 500;
            display: flex;
            align-items: center;
            gap: 8px;
            border: 1px solid rgba(255, 255, 255, 0.1);
            opacity: 0.9;
        }
        #image-toggler-ui:hover {
            opacity: 1;
            transform: translateY(2px);
            box-shadow: 0 6px 20px rgba(0,0,0,0.4);
        }
        #image-toggler-ui.dragging {
            cursor: grabbing;
            box-shadow: 0 8px 25px rgba(0,0,0,0.5);
            transform: scale(1.05);
            z-index: 2147483647;
        }
        #image-toggler-ui.hidden-mode {
            background-color: rgba(69, 105, 212, 0.9);
            border-color: rgba(69, 105, 212, 0.5);
        }
        #image-toggler-ui.visible-mode {
            background-color: rgba(69, 105, 212, 0.9);
            border-color: rgba(69, 105, 212, 0.5);
        }
        #image-toggler-icon {
            font-size: 16px;
            line-height: 1;
        }
        #image-toggler-text {
            line-height: 1;
        }
    `;

    // 初始化
    function init() {
        // 尽早应用状态(如果需要隐藏图片)
        applyState();

        // 等待 body 加载完成后创建 UI
        if (document.body) {
            setupUI();
            applyUIState();
        } else {
            document.addEventListener('DOMContentLoaded', setupUI);
            document.addEventListener('DOMContentLoaded', applyUIState);
        }

        // 注册菜单命令作为备用
        GM_registerMenuCommand("切换图片显示/隐藏", toggleImages);
        GM_registerMenuCommand("切换控件显示/隐藏", toggleUIVisibility);

        // 添加键盘快捷键 Ctrl+Shift+I
        document.addEventListener('keydown', function(e) {
            if (e.ctrlKey && e.shiftKey && e.key === 'I') {
                e.preventDefault();
                toggleImages();
            }
        });
    }

    function setupUI() {
        // 防止重复创建
        if (document.getElementById('image-toggler-ui') && document.getElementById('image-toggler-ui-top')) return;

        // 添加 UI 样式
        GM_addStyle(uiCSS);

        // 创建 UI
        createUI();
    }

    // 创建 UI 控件
    function createUI() {
        const div = document.createElement('div');
        div.id = 'image-toggler-ui';
        div.style.top = uiPosition.top + 'px';
        div.style.right = uiPosition.right + 'px';

        let isDragging = false;
        let dragOffsetX = 0;
        let dragOffsetY = 0;

        div.addEventListener('mousedown', function(e) {
            if (e.button !== 0) return;
            const rect = div.getBoundingClientRect();
            dragOffsetX = e.clientX - rect.left;
            dragOffsetY = e.clientY - rect.top;
            isDragging = true;
            div.classList.add('dragging');
            e.preventDefault();
        });

        document.addEventListener('mousemove', function(e) {
            if (!isDragging) return;
            const newLeft = e.clientX - dragOffsetX;
            const newTop = e.clientY - dragOffsetY;
            div.style.right = 'auto';
            div.style.left = newLeft + 'px';
            div.style.top = newTop + 'px';
        });

        document.addEventListener('mouseup', function(e) {
            if (!isDragging) return;
            isDragging = false;
            div.classList.remove('dragging');

            const rect = div.getBoundingClientRect();
            const finalLeft = rect.left;
            const finalTop = rect.top;

            uiPosition = { top: finalTop, right: window.innerWidth - finalLeft - rect.width };
            GM_setValue(positionKey, uiPosition);

            div.style.left = 'auto';
            div.style.right = uiPosition.right + 'px';
        });

        div.addEventListener('click', function(e) {
            if (isDragging) {
                e.preventDefault();
                e.stopPropagation();
                return;
            }
            toggleImages();
        });

        div.title = '拖动调整位置 | 点击切换图片显隐 | Ctrl+Shift+I 快捷键切换';
        document.body.appendChild(div);
        updateUI(div);

        // 创建右上角 UI
    }

    // 更新 UI 显示
    function updateUI(element) {
        const el = element || document.getElementById('image-toggler-ui');
        if (!el) return;

        // 移除旧的类
        el.classList.remove('visible-mode', 'hidden-mode');

        if (isImagesVisible) {
            el.innerHTML = '<span id="image-toggler-icon">👁️</span> <span id="image-toggler-text">显</span>';
            el.classList.add('visible-mode');
            el.title = '当前:图片显示。点击隐藏图片';
        } else {
            el.innerHTML = '<span id="image-toggler-icon">🚫</span> <span id="image-toggler-text">隐</span>';
            el.classList.add('hidden-mode');
            el.title = '当前:图片隐藏。点击显示图片';
        }
        // 不需要在这里直接设置 backgroundColor,交由 CSS 类处理
        el.style.backgroundColor = '';
    }

    // 切换状态
    function toggleImages() {
        isImagesVisible = !isImagesVisible;
        GM_setValue(storageKey, isImagesVisible);
        applyState();
        updateUI();
    }

    // 切换 UI 可见性
    function toggleUIVisibility() {
        isUIVisible = !isUIVisible;
        GM_setValue(UI_VISIBLE_KEY, isUIVisible);
        applyUIState();
    }

    // 应用 UI 可见性状态
    function applyUIState() {
        const ui = document.getElementById('image-toggler-ui');
        if (ui) {
            ui.style.display = isUIVisible ? 'flex' : 'none';
        }
    }

    // 应用状态(添加或移除 CSS)
    function applyState() {
        let styleEl = document.getElementById(STYLE_ID);

        // 确保 head 存在,通常在 document-start 时 head 也是存在的(除了极早的情况)
        // 如果 head 不存在,稍微延时重试
        if (!document.head) {
            setTimeout(applyState, 10);
            return;
        }

        if (!isImagesVisible) {
            // 如果需要隐藏图片,且样式元素不存在,则添加
            if (!styleEl) {
                styleEl = document.createElement('style');
                styleEl.id = STYLE_ID;
                styleEl.textContent = hideImageCSS;
                document.head.appendChild(styleEl);
            }
        } else {
            // 如果需要显示图片,且样式元素存在,则移除
            if (styleEl) {
                styleEl.remove();
            }
        }
    }

    // 启动
    init();

})();