Greasy Fork

Greasy Fork is available in English.

更好用的脚本!控制页面的图片显示与隐藏

稳定版图片隐藏/显示工具,解决错位问题,确保所有图片显示控制按钮,图片消失时自动清理控制元素

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

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

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

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

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

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

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

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

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

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

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

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

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

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

// ==UserScript==
// @name         更好用的脚本!控制页面的图片显示与隐藏
// @version      6.2
// @description  稳定版图片隐藏/显示工具,解决错位问题,确保所有图片显示控制按钮,图片消失时自动清理控制元素
// @author       hulada
// @match        *://*/*
// @grant        none
// @license      MIT
// @run-at       document-body
// @namespace http://greasyfork.icu/users/1043548
// ==/UserScript==

(function () {
    let active = false;
    let currentX, currentY, initialX, initialY, xOffset = 0, yOffset = 0, startTime = 0;

    window.imgHidenSet = null;
    window.imgShownSet = null;

    let yanEmoji = ["(‐^▽^‐)"];
    let randomNumSet;

    // 存储所有图片元素及其对应的控制按钮和轮廓
    let imageControls = new Map();
    let imageOutlines = new Map();
    let mouseTimers = new Map();

    // 自动隐藏延迟时间(毫秒)
    const AUTO_HIDE_DELAY = 500;

    // 简单的节流函数
    function throttle(func, delay) {
        let timeout;
        return function(...args) {
            if (!timeout) {
                timeout = setTimeout(() => {
                    func.apply(this, args);
                    timeout = null;
                }, delay);
            }
        };
    }

    let toggleSingleImage = function(img, button, outline) {
        if (img.style.visibility === 'hidden' || img.style.visibility === '') {
            img.style.visibility = 'visible';
            img.style.transition = 'filter 0.6s ease';
            img.style.filter = 'none';
            outline.style.display = 'none';
            button.innerText = '隐';
            button.title = '点击隐藏图片';
        } else {
            img.style.visibility = 'hidden';
            img.style.transition = 'filter 0.6s ease';
            img.style.filter = 'blur(14px)';
            outline.style.display = 'block';
            button.innerText = '显';
            button.title = '点击显示图片';
        }
    };

    let startAutoHideTimer = function(button, outline) {
        clearAutoHideTimer(button);
        mouseTimers.set(button, setTimeout(() => {
            button.style.opacity = '0.35';
            outline.style.opacity = '0.35';
        }, AUTO_HIDE_DELAY));
    };

    let clearAutoHideTimer = function(button) {
        if (mouseTimers.has(button)) {
            clearTimeout(mouseTimers.get(button));
            mouseTimers.delete(button);
        }
    };

    let createImageOutline = function(img) {
        if (imageOutlines.has(img)) return imageOutlines.get(img);

        let outline = document.createElement('div');
        outline.style.position = 'absolute';
        outline.style.zIndex = '999998';
        outline.style.border = '2px dashed rgba(128, 128, 128, 0.6)';
        outline.style.pointerEvents = 'none';
        outline.style.transition = 'opacity 0.3s ease';
        outline.style.display = 'none';

        document.body.appendChild(outline);
        imageOutlines.set(img, outline);
        return outline;
    };

    // 清理已消失的图片控制元素
    let cleanupOrphanedControls = function() {
        const controlsToRemove = [];
        const outlinesToRemove = [];

        // 检查每个图片是否仍然存在且可见
        imageControls.forEach((button, img) => {
            if (!img || !img.isConnected || img.offsetWidth === 0 || img.offsetHeight === 0) {
                controlsToRemove.push(img);
            }
        });

        // 清理孤立的控制元素
        controlsToRemove.forEach(img => {
            const button = imageControls.get(img);
            const outline = imageOutlines.get(img);

            if (button) {
                // 清理定时器
                clearAutoHideTimer(button);

                // 移除DOM元素
                if (button.parentNode) {
                    button.parentNode.removeChild(button);
                }
                imageControls.delete(img);
            }

            if (outline) {
                if (outline.parentNode) {
                    outline.parentNode.removeChild(outline);
                }
                imageOutlines.delete(img);
            }
        });
    };

    // 简化的位置更新(包含清理功能)
    let updatePositions = throttle(function() {
        // 先清理孤立的控制元素
        cleanupOrphanedControls();

        imageControls.forEach((button, img) => {
            if (!img || !img.isConnected) return;

            const rect = img.getBoundingClientRect();
            const scrollX = window.pageXOffset || document.documentElement.scrollLeft;
            const scrollY = window.pageYOffset || document.documentElement.scrollTop;

            // 检查图片是否仍然可见
            if (rect.width === 0 || rect.height === 0 || img.offsetWidth === 0 || img.offsetHeight === 0) {
                return;
            }

            // 更新按钮位置
            button.style.left = (rect.left + scrollX + 8) + 'px';
            button.style.top = (rect.top + scrollY + 8) + 'px';

            // 更新轮廓位置
            const outline = imageOutlines.get(img);
            if (outline) {
                outline.style.left = (rect.left + scrollX - 3) + 'px';
                outline.style.top = (rect.top + scrollY - 3) + 'px';
                outline.style.width = (rect.width + 4) + 'px';
                outline.style.height = (rect.height + 4) + 'px';
            }
        });
    }, 200);

    let createImageControlButton = function(img) {
        if (imageControls.has(img)) return;

        let button = document.createElement('div');
        button.innerText = '显';
        button.title = '点击隐藏图片';

        let outline = createImageOutline(img);

        // 样式设置
        button.style.position = 'absolute';
        button.style.zIndex = '999999';
        button.style.padding = '4px 8px';
        button.style.background = 'rgba(96, 96, 96, 0.8)';
        button.style.border = '1px solid rgba(128, 128, 128, 0.9)';
        button.style.color = 'rgba(240, 240, 240, 1)';
        button.style.fontSize = '12px';
        button.style.fontFamily = 'Arial, sans-serif';
        button.style.lineHeight = '1.2';
        button.style.textAlign = 'center';
        button.style.cursor = 'pointer';
        button.style.borderRadius = '4px';
        button.style.transition = 'opacity 0.3s ease';
        button.style.userSelect = 'none';

        // 初始位置
        updatePositions();

        // 事件处理
        button.addEventListener('mouseenter', function() {
            clearAutoHideTimer(button);
            button.style.opacity = '1';
            button.style.background = 'rgba(128, 128, 128, 0.9)';
            outline.style.opacity = '1';
        });

        button.addEventListener('mouseleave', function() {
            startAutoHideTimer(button, outline);
            button.style.background = 'rgba(96, 96, 96, 0.8)';
            outline.style.opacity = '0.3';
        });

        button.addEventListener('click', function(e) {
            e.preventDefault();
            e.stopPropagation();
            toggleSingleImage(img, button, outline);
            clearAutoHideTimer(button);
            button.style.opacity = '1';
        });

        document.body.appendChild(button);
        imageControls.set(img, button);

        // 初始状态
        if (img.style.visibility !== 'hidden') {
            img.style.visibility = 'hidden';
            img.style.transition = 'filter 0.6s ease';
            img.style.filter = 'blur(14px)';
            outline.style.display = 'block';
        }

        startAutoHideTimer(button, outline);
    };

    // 查找所有图片
    let findAllImages = function() {
        let images = [];

        // 标准图片
        document.querySelectorAll('img').forEach(img => {
            if (!imageControls.has(img) && img.offsetWidth > 0 && img.offsetHeight > 0) {
                images.push(img);
            }
        });

        return images;
    };

    let imgHiden = function() {
        const images = findAllImages();
        images.forEach(img => {
            createImageControlButton(img);
        });

        // 更新所有位置并清理孤立的控制元素
        updatePositions();
    };

    let imgShown = function() {
        imageControls.forEach((button, img) => {
            img.style.visibility = 'visible';
            img.style.filter = 'none';
            if (button.parentNode) {
                button.parentNode.removeChild(button);
            }
        });

        imageOutlines.forEach((outline) => {
            if (outline.parentNode) {
                outline.parentNode.removeChild(outline);
            }
        });

        mouseTimers.forEach(timer => clearTimeout(timer));
        mouseTimers.clear();
        imageControls.clear();
        imageOutlines.clear();
    };

    // 启动位置更新监听
    let startPositionUpdates = function() {
        // 监听滚动
        window.addEventListener('scroll', updatePositions, { passive: true });
        // 监听窗口大小变化
        window.addEventListener('resize', updatePositions, { passive: true });
        // 监听图片加载
        document.addEventListener('load', function(e) {
            if (e.target.tagName === 'IMG') {
                setTimeout(updatePositions, 100);
            }
        }, true);
    };

    let handleButtonClick = function() {
        if (window.imgHidenSet === null) {
            clearInterval(window.imgShownSet);
            window.imgShownSet = null;
            startPositionUpdates();
            imgHiden();
            saveStorageList();
            window.imgHidenSet = setInterval(imgHiden, 500);
        } else {
            clearInterval(window.imgHidenSet);
            window.imgHidenSet = null;
            imgShown();
            deleteStorageList();
            window.imgShownSet = setInterval(imgShown, 500);
        }
    };

    let noDetectStorageList = function() {
        if (localStorage.getItem('nopicValueList') !== null) {
            let valueList = localStorage.getItem('nopicValueList').split(',');
            if (valueList.indexOf(location.host) === -1) return true;
        } else {
            localStorage.setItem('nopicValueList', ['fxalll']);
            return true;
        }
        return false;
    };

    let saveStorageList = function() {
        if (noDetectStorageList()) {
            let valueList = localStorage.getItem('nopicValueList').split(',');
            if (!valueList.includes(location.host)) {
                valueList.push(location.host);
                localStorage.setItem('nopicValueList', valueList);
            }
        }
    };

    let deleteStorageList = function() {
        if (!noDetectStorageList()) {
            let valueList = localStorage.getItem('nopicValueList').split(',');
            valueList = valueList.filter(value => value !== location.host);
            localStorage.setItem('nopicValueList', valueList);
        }
    };

    // 初始化
    if (!noDetectStorageList()) {
        startPositionUpdates();
        imgHiden();
        window.imgHidenSet = setInterval(imgHiden, 500);
    }

    let button = document.createElement('div');
    button.innerText = "◀";
    button.setAttribute("id", "myButton");
    button.style.color = "#555";
    button.style.padding = "10px 15px";
    button.style.position = "fixed";
    button.style.top = "50%";
    button.style.left = "3px";
    button.style.textAlign = "center";
    button.style.alignContent = "center";
    button.style.background = "rgba(128, 128, 128, 0.4)";
    button.style.borderRadius = "15px";
    button.style.border = "2px solid rgba(128, 128, 128, 0.7)";
    button.style.cursor = "pointer";
    button.style.transform = "translate3d(30%,0,0)";
    button.style.transition = "all 0.1s ease, backdrop-filter 0.4s ease, background 0.4s ease, box-shadow 0.4s ease, opacity 0.4s ease";
    button.style.backdropFilter = "blur(15px) saturate(1.2)";
    button.style.webkitBackdropFilter = "blur(15px) saturate(1.2)";
    button.style.userSelect = "none";
    button.style.zIndex = "999999999999";

    button.addEventListener('mouseover', mouseover);
    button.addEventListener('mouseout', mouseout);
    button.addEventListener('mousedown', dragStart, false);

    function mouseover() {
        button.style.boxShadow = "0 0 20px rgba(128, 128, 128, 0.8)";
        button.style.background = "rgba(160, 160, 160, 0.8)";
        button.style.color = "#fff";
        button.style.border = "2px solid rgba(200, 200, 200, 1)";
        button.style.backdropFilter = "blur(20px) saturate(1.5)";
        button.style.webkitBackdropFilter = "blur(20px) saturate(1.5)";
        button.innerText = "图片显隐";
        button.style.transform = "translateX(0px)";
        setTranslate(0+"%",currentY+"px",button);
    }

    function mouseout() {
        button.style.boxShadow = '';
        button.style.background = "rgba(128, 128, 128, 0.3)";
        button.style.color = "#555";
        button.style.border = "2px solid rgba(128, 128, 128, 0.6)";
        button.style.backdropFilter = "blur(15px) saturate(1.2)";
        button.style.webkitBackdropFilter = "blur(15px) saturate(1.2)";
        button.innerText = "◀";
        button.style.transform = "translateX(30%)";
        setTranslate(30+"%",currentY+"px",button);
    }

    function dragStart(e) {
        startTime = e.timeStamp;
        initialX = e.clientX;
        initialY = e.clientY - yOffset;
        document.addEventListener('mousemove', drag);
        document.addEventListener('mouseup', dragEnd, false);
        e.preventDefault();
        randomNumSet = Math.round(Math.random()*(yanEmoji.length-1));
    }

    function dragEnd(e) {
        if (startTime) {
            let diffTime = e.timeStamp - startTime;
            diffTime < 150 && handleButtonClick();
            startTime = 0;
        }
        initialX = currentX;
        initialY = currentY;
        mouseout();
        document.removeEventListener('mousemove', drag);
    }

    function drag(e) {
        changeFace(randomNumSet,button);
        button.style.color = "#fff";
        button.style.border = "4px dashed rgba(128, 128, 128, 0.8)";
        button.style.background = "rgba(100, 100, 100, 0.6)";
        button.style.backdropFilter = "blur(25px) saturate(1.8)";
        button.style.webkitBackdropFilter = "blur(25px) saturate(1.8)";
        e.preventDefault();
        currentX = e.clientX - initialX;
        currentY = e.clientY - initialY;
        xOffset = currentX;
        yOffset = currentY;
        setTranslate(currentX+"px", currentY+"px", button);
    }

    function setTranslate(xPos, yPos, el) {
        el.style.transform = "translate3d(" + xPos + ", " + yPos + ", 0)";
    }

    function changeFace(randomNum,el) {
        el.innerText = yanEmoji[randomNum];
    }

    document.body.appendChild(button);
})();