图片悬浮放大工具

在任何网页上悬浮图片时显示放大图标,点击可放大查看,支持拖拽和缩放,优化交互体验

// ==UserScript==
// @name         图片悬浮放大工具
// @namespace    http://tampermonkey.net/
// @version      1.2
// @description  在任何网页上悬浮图片时显示放大图标,点击可放大查看,支持拖拽和缩放,优化交互体验
// @author       kognling
// @match        *://*/*
// @grant        GM_addStyle
// @license      MIT
// ==/UserScript==

(function() {
    'use strict';

    // 添加CSS样式
    GM_addStyle(`
        .image-zoom-icon {
            position: absolute;
            top: 5px;
            right: 5px;
            width: 24px;
            height: 24px;
            //background-color: rgba(0, 0, 0, 0.6);
            border-radius: 50%;
            display: flex;
            align-items: center;
            justify-content: center;
            color: white;
            font-size: 14px;
            cursor: pointer;
            opacity: 0;
            transition: opacity 0.3s;
            z-index: 9998;
            pointer-events: auto;
        }

        .image-zoom-icon:hover {
            //background-color: rgba(0, 0, 0, 0.8);
        }

        .image-container:hover .image-zoom-icon {
            opacity: 1;
        }

        .image-container {
            position: relative;
            display: inline-block;
        }

        .zoom-overlay {
            position: fixed;
            top: 0;
            left: 0;
            width: 100%;
            height: 100%;
            background-color: rgba(0, 0, 0, 0.8);
            display: flex;
            justify-content: center;
            align-items: center;
            z-index: 9999;
            cursor: grab;
        }

        .zoom-overlay.grabbing {
            cursor: grabbing;
        }

        .zoom-content {
            position: relative;
            max-width: 90%;
            max-height: 90%;
            transition: transform 0.3s;
            transform-origin: center center;
        }

        .zoom-close {
            position: absolute;
            top: 20px;
            right: 20px;
            width: 40px;
            height: 40px;
            background-color: rgba(0, 0, 0, 0.6);
            border-radius: 50%;
            display: flex;
            align-items: center;
            justify-content: center;
            color: white;
            font-size: 20px;
            cursor: pointer;
            z-index: 10000;
        }

        .zoom-close:hover {
            background-color: rgba(0, 0, 0, 0.8);
        }

        .zoomed-image {
            max-width: 100%;
            max-height: 90vh;
            object-fit: contain;
            pointer-events: none;
        }

        /* 隐藏放大后图片上的放大按钮 */
        .zoomed-image-container .image-zoom-icon {
            display: none !important;
        }
    `);

    // 为所有图片添加容器和放大图标
    function addZoomIcons() {
        const images = document.querySelectorAll('img:not([data-zoom-processed]):not(.zoomed-image)');

        images.forEach(img => {
            // 跳过很小的图片(比如图标)
            if (img.width < 50 || img.height < 50) return;

            // 标记已处理
            img.setAttribute('data-zoom-processed', 'true');

            // 创建容器
            const container = document.createElement('div');
            container.className = 'image-container';

            // 包装图片
            img.parentNode.insertBefore(container, img);
            container.appendChild(img);

            // 添加放大图标
            const zoomIcon = document.createElement('div');
            zoomIcon.className = 'image-zoom-icon';
            zoomIcon.innerHTML = '<svg t="1754016487003" class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="2732" width="16" height="16"><path d="M919.920093 725.414549q3.014188 26.122962 7.033105 58.776664t7.53547 66.814498 7.53547 67.819227 7.033105 60.786122q6.028376 47.222277-41.193901 44.208089-25.118232-2.009459-56.767205-5.526011t-64.805039-7.53547-65.809769-8.037834-59.781393-7.033105q-29.137149-3.014188-37.174984-16.578033t9.042564-30.644243q11.052022-10.047293 27.127691-27.630056t27.127691-28.634785q11.052022-12.056752 7.033105-22.104044t-16.075669-23.108774q-28.13242-27.127691-51.241194-49.231735t-51.241194-51.241194q-6.028376-6.028376-12.056752-13.061481t-9.042564-15.573304-1.004729-18.085127 13.061481-20.59695q6.028376-6.028376 10.047293-10.549658t8.037834-8.037834 8.540199-8.037834 11.554387-12.559116q20.094586-20.094586 37.174984-17.080398t37.174984 23.108774 41.193901 40.691536 47.222277 46.719912q19.089857 18.085127 32.653702 25.118232t26.625326-6.028376q9.042564-9.042564 22.606409-21.60168t23.611138-22.606409q17.080398-17.080398 30.644243-13.061481t16.578033 30.141879zM43.79615 383.80659q-3.014188-26.122962-7.033105-58.776664t-7.53547-66.814498-7.53547-67.819227-7.033105-60.786122q-3.014188-26.122962 6.53074-36.170255t33.658431-8.037834q25.118232 2.009459 56.767205 5.526011t64.805039 7.53547 65.809769 8.037834 59.781393 7.033105q30.141879 3.014188 37.677348 16.578033t-9.544928 30.644243q-10.047293 10.047293-24.615868 26.122962t-25.620597 27.127691q-12.056752 12.056752-8.037834 22.104044t17.080398 23.108774q13.061481 14.06621 24.615868 24.615868t22.606409 21.099315 23.108774 22.606409l25.118232 25.118232q6.028376 6.028376 11.554387 14.06621t8.037834 17.080398-0.502365 19.089857-13.061481 20.094586l-11.052022 11.052022q-4.018917 4.018917-7.53547 8.037834t-8.540199 8.037834l-11.052022 12.056752q-20.094586 20.094586-34.663161 15.070939t-34.663161-25.118232-38.179713-37.677348-44.208089-43.705724q-18.085127-18.085127-32.151337-25.118232t-27.127691 6.028376q-9.042564 10.047293-25.118232 24.615868t-26.122962 24.615868q-17.080398 17.080398-30.141879 13.061481t-16.075669-30.141879zM905.853883 84.397261q26.122962-3.014188 36.170255 6.53074t8.037834 34.663161-5.526011 56.767205-7.53547 64.805039-8.037834 65.809769-7.033105 59.781393q-3.014188 29.137149-16.578033 37.174984t-30.644243-10.047293q-10.047293-10.047293-26.122962-24.615868t-27.127691-25.620597q-12.056752-11.052022-22.104044-7.53547t-23.108774 16.578033q-27.127691 27.127691-47.724641 49.231735t-48.729371 50.236465q-6.028376 6.028376-14.06621 11.554387t-17.080398 8.037834-19.089857-0.502365-20.094586-14.06621q-6.028376-6.028376-10.549658-10.047293t-8.540199-8.037834-8.540199-8.037834-11.554387-12.056752q-20.094586-20.094586-16.075669-35.165525t25.118232-35.165525l38.179713-40.189172q19.089857-20.094586 45.212818-46.217547 19.089857-18.085127 26.122962-32.151337t-7.033105-26.122962q-9.042564-9.042564-23.108774-24.615868t-24.113503-25.620597q-17.080398-17.080398-13.061481-30.141879t30.141879-16.075669 58.776664-7.033105 67.316863-7.53547 67.819227-7.53547 60.283758-7.033105zM350.238584 640.012559q6.028376 6.028376 10.549658 10.047293t8.540199 8.037834l8.037834 9.042564 12.056752 11.052022q20.094586 20.094586 17.582763 36.672619t-23.611138 37.677348q-19.089857 19.089857-40.189172 40.691536t-47.222277 47.724641q-18.085127 18.085127-22.606409 29.639514t8.540199 24.615868q10.047293 9.042564 22.606409 22.606409t22.606409 23.611138q17.080398 17.080398 12.559116 30.141879t-30.644243 16.075669-58.274299 7.033105-66.814498 8.037834-68.321592 8.037834-60.786122 7.033105q-25.118232 2.009459-35.66789-7.53547t-8.540199-33.658431q2.009459-25.118232 5.526011-56.767205t7.53547-64.805039 8.037834-65.809769 7.033105-59.781393q3.014188-30.141879 16.578033-37.677348t30.644243 9.544928q10.047293 10.047293 27.630056 26.122962t28.634785 27.127691q12.056752 12.056752 20.094586 10.549658t20.094586-14.568575q13.061481-13.061481 25.118232-25.620597t24.113503-24.615868 24.615868-25.118232 26.625326-27.127691q6.028376-6.028376 13.061481-12.056752t15.573304-9.042564 18.085127-0.502365 20.59695 13.563845z" p-id="2733"></path></svg>';
            container.appendChild(zoomIcon);

            // 添加点击事件
            zoomIcon.addEventListener('click', (e) => {
                e.preventDefault();
                e.stopPropagation();
                e.stopImmediatePropagation();
                showZoomedImage(img);
            }, true);
        });
    }

    // 显示放大后的图片
    function showZoomedImage(img) {
        // 创建遮罩层
        const overlay = document.createElement('div');
        overlay.className = 'zoom-overlay';

        // 创建内容容器
        const content = document.createElement('div');
        content.className = 'zoom-content';

        // 创建关闭按钮
        const closeBtn = document.createElement('div');
        closeBtn.className = 'zoom-close';
        closeBtn.innerHTML = '×';

        // 创建放大后的图片容器
        const zoomedImgContainer = document.createElement('div');
        zoomedImgContainer.className = 'image-container zoomed-image-container';

        // 创建放大后的图片
        const zoomedImg = document.createElement('img');
        zoomedImg.className = 'zoomed-image';
        zoomedImg.src = img.src;

        // 组装元素
        zoomedImgContainer.appendChild(zoomedImg);
        content.appendChild(zoomedImgContainer);
        overlay.appendChild(content);
        overlay.appendChild(closeBtn);
        document.body.appendChild(overlay);

        // 禁止滚动
        document.body.style.overflow = 'hidden';

        // 当前缩放比例和位置
        let scale = 1;
        let posX = 0;
        let posY = 0;
        let isDragging = false;
        let startX, startY;

        // 关闭函数
        function closeZoom() {
            document.body.removeChild(overlay);
            document.body.style.overflow = '';
        }

        // 点击遮罩层关闭
        overlay.addEventListener('click', (e) => {
            if (e.target === overlay && !isDragging) {
                closeZoom();
            }
            isDragging = false;
            overlay.classList.remove('grabbing');
        });

        // 点击关闭按钮关闭
        closeBtn.addEventListener('click', closeZoom);

        // 鼠标按下开始拖拽
        content.addEventListener('mousedown', (e) => {
            if (e.button !== 0) return; // 只响应左键
            isDragging = true;
            startX = e.clientX - posX;
            startY = e.clientY - posY;
            overlay.classList.add('grabbing');
            e.preventDefault();
        });

        // 鼠标移动处理拖拽
        overlay.addEventListener('mousemove', (e) => {
            if (!isDragging) return;

            posX = e.clientX - startX;
            posY = e.clientY - startY;

            content.style.transform = `translate(${posX}px, ${posY}px) scale(${scale})`;
        });

        // 鼠标释放结束拖拽
        overlay.addEventListener('mouseup', () => {
            isDragging = false;
            overlay.classList.remove('grabbing');
        });

        // 鼠标离开窗口结束拖拽
        overlay.addEventListener('mouseleave', () => {
            isDragging = false;
            overlay.classList.remove('grabbing');
        });

        // 滚轮缩放
        content.addEventListener('wheel', (e) => {
            e.preventDefault();

            // 计算缩放比例
            const delta = e.deltaY > 0 ? -0.1 : 0.1;
            scale = Math.max(0.1, scale + delta);

            // 应用缩放
            content.style.transform = `translate(${posX}px, ${posY}px) scale(${scale})`;
        });

        // 键盘ESC关闭
        document.addEventListener('keydown', (e) => {
            if (e.key === 'Escape') {
                closeZoom();
            }
        }, { once: true });
    }

    // 初始添加图标
    addZoomIcons();

    // 使用MutationObserver监听动态加载的图片
    const observer = new MutationObserver((mutations) => {
        mutations.forEach((mutation) => {
            if (mutation.addedNodes) {
                addZoomIcons();
            }
        });
    });

    observer.observe(document.body, {
        childList: true,
        subtree: true
    });
})();