图片放大油猴脚本(悬浮按钮,拖动,旋转,翻页)

点击图片放大到屏幕显示的最大尺寸,并支持滚轮放大、拖动和旋转,点击关闭放大显示的图片,并增加切换上一张或下一张图片的功能。

질문, 리뷰하거나, 이 스크립트를 신고하세요.
// ==UserScript==
// @name         图片放大油猴脚本(悬浮按钮,拖动,旋转,翻页)
// @namespace    http://your.namespace.com
// @version      3.0
// @description  点击图片放大到屏幕显示的最大尺寸,并支持滚轮放大、拖动和旋转,点击关闭放大显示的图片,并增加切换上一张或下一张图片的功能。
// @author       肆散的尘埃i
// @match        https://happy.5ge.net/*
// @match        http*://*/*
// @grant        GM_addStyle
// ==/UserScript==

(function () {
    'use strict';

    // 自动执行URL列表
    var urlList = [
        'https://happy.5ge.net',
        'https://www.sstuku26.xyz'
    ];

    // 添加样式
    function addStyles() {
        GM_addStyle(`
            .gm-expanded-image-container {
                position: fixed;
                top: 0;
                left: 0;
                width: 100%;
                height: 100%;
                background-color: rgba(0, 0, 0, 0.8);
                z-index: 9999;
                display: none;
                justify-content: center;
                align-items: center;
                overflow: hidden;
            }
            .gm-expanded-image {
                max-width: 100%;
                max-height: 100%;
                cursor: grab;
                user-select: none;
                -webkit-user-drag: none;
                transform-origin: center center;
                position: absolute;
            }
            .gm-floating-button {
                position: fixed;
                bottom: 20px;
                left: 20px;
                z-index: 10000;
                width: 50px;
                height: 50px;
                background-color: transparent; /* 底色改为透明 */
                color: #333; /* 设置字体颜色为深灰色 */
                display: flex;
                justify-content: center;
                align-items: center;
                cursor: pointer;
                font-size: 24px;
                border-radius: 50%;
                border: 2px solid #333; /* 设置边框颜色为深灰色 */
                user-select: none;
            }
            .gm-rotate-left, .gm-rotate-right, .gm-close-button, .gm-prev-button, .gm-next-button {
                position: fixed;
                z-index: 10001;
                width: 50px;
                height: 50px;
                background-color: #333;
                color: white;
                display: flex;
                justify-content: center;
                align-items: center;
                cursor: pointer;
                font-size: 24px;
                border-radius: 50%;
                border: 2px solid #fff;
                display: none;
            }
            .gm-rotate-left {
                bottom: 20px;
                right: 140px;
            }
            .gm-rotate-right {
                bottom: 20px;
                right: 80px;
            }
            .gm-close-button {
                top: 20px;
                right: 20px;
            }
            .gm-prev-button {
                top: 50%;
                left: 20px;
                transform: translateY(-50%);
            }
            .gm-next-button {
                top: 50%;
                right: 20px;
                transform: translateY(-50%);
            }
        `);
    }

    // 添加悬浮图片按钮
    function addFloatingButton() {
        var $floatingButton = document.createElement('div');
        $floatingButton.classList.add('gm-floating-button');
        $floatingButton.textContent = '🔍';
        document.body.appendChild($floatingButton);

        // 点击悬浮按钮执行 disableImageClick 操作
        $floatingButton.addEventListener('click', function () {
            show_message("悬浮图片被点击~");
            // document.getElementById("adver_box") ? .remove();
            disableImageClick();
        });

        // 使悬浮按钮可拖动
        makeElementDraggable($floatingButton);
    }

    // 使元素可拖动
    function makeElementDraggable(element) {
        var isDragging = false;
        var lastX = 0;
        var lastY = 0;

        element.addEventListener('mousedown', function (e) {
            isDragging = true;
            lastX = e.clientX;
            lastY = e.clientY;
            element.style.cursor = 'grabbing';
            e.preventDefault();
        });

        document.addEventListener('mousemove', function (e) {
            if (isDragging) {
                var dx = e.clientX - lastX;
                var dy = e.clientY - lastY;
                var rect = element.getBoundingClientRect();

                element.style.left = rect.left + dx + 'px';
                element.style.top = rect.top + dy + 'px';

                lastX = e.clientX;
                lastY = e.clientY;
            }
        });

        document.addEventListener('mouseup', function () {
            if (isDragging) {
                isDragging = false;
                element.style.cursor = 'grab';
            }
        });
    }

    var $expandedImage;
    var $rotateLeftButton;
    var $rotateRightButton;
    var $closeButton;
    var $prevButton;
    var $nextButton;
    var scale = 1;
    var rotation = 0;
    var translateX = 0;
    var translateY = 0;
    var lastX = 0;
    var lastY = 0;
    var currentIndex = -1;
    var images = [];

    // 屏蔽图片默认点击功能
    function disableImageClick() {
        images = Array.from(document.getElementsByTagName('img'));
        for (var i = 0; i < images.length; i++) {
            if (!images[i].hasAttribute('data-gm-enlargeable')) {
                images[i].setAttribute('data-gm-enlargeable', 'true');
                images[i].addEventListener('click', function (e) {
                    if (e.target.classList.contains('gm-floating-button')) {
                        return; // 如果是悬浮按钮则不执行放大功能
                    }
                    e.preventDefault();
                    e.stopPropagation();
                    currentIndex = images.indexOf(e.target);
                    enlargeImage(e.target); // 放大图片
                });
            }
        }
    }

    // 添加放大功能
    function enlargeImage(imgElement) {
        if (document.querySelector('.gm-expanded-image-container')) {
            closeEnlargedImage(document.querySelector('.gm-expanded-image-container'));
        }

        var src = imgElement.src;
        var $expandedContainer = document.createElement('div');
        $expandedContainer.classList.add('gm-expanded-image-container');

        $expandedImage = document.createElement('img');
        $expandedImage.classList.add('gm-expanded-image');
        $expandedImage.src = src;

        $expandedContainer.appendChild($expandedImage);
        document.body.appendChild($expandedContainer);
        $expandedContainer.style.display = 'flex';

        // 禁止原始图片的点击行为
        imgElement.style.pointerEvents = 'none';

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

        // 点击遮罩层关闭放大图片
        $expandedContainer.addEventListener('click', function (e) {
            if (e.target === $expandedContainer) {
                closeEnlargedImage($expandedContainer);
            }
        });

        // 按Esc键关闭放大图片
        document.addEventListener('keydown', function onEscPress(e) {
            if (e.key === 'Escape') {
                closeEnlargedImage($expandedContainer);
                document.removeEventListener('keydown', onEscPress);
            }
        });

        // 键盘方向键功能
        document.addEventListener('keydown', function onArrowPress(e) {
            if (e.key === 'ArrowLeft') {
                showPrevImage();
            } else if (e.key === 'ArrowRight') {
                showNextImage();
            } else if (e.key === 'ArrowUp') {
                rotation -= 90;
                $expandedImage.style.transform = `scale(${scale}) rotate(${rotation}deg) translate(${translateX}px, ${translateY}px)`;
            } else if (e.key === 'ArrowDown') {
                rotation += 90;
                $expandedImage.style.transform = `scale(${scale}) rotate(${rotation}deg) translate(${translateX}px, ${translateY}px)`;
            }
        });

        // 鼠标拖动
        $expandedImage.addEventListener('mousedown', function (e) {
            e.preventDefault();
            lastX = e.clientX;
            lastY = e.clientY;

            function onMouseMove(e) {
                e.preventDefault();
                var dx = e.clientX - lastX;
                var dy = e.clientY - lastY;
                translateX += dx;
                translateY += dy;
                $expandedImage.style.transform = `scale(${scale}) rotate(${rotation}deg) translate(${translateX}px, ${translateY}px)`;
                lastX = e.clientX;
                lastY = e.clientY;
            }

            function onMouseUp() {
                document.removeEventListener('mousemove', onMouseMove);
                document.removeEventListener('mouseup', onMouseUp);
            }

            document.addEventListener('mousemove', onMouseMove);
            document.addEventListener('mouseup', onMouseUp);
        });

        // 滚轮缩放
        $expandedImage.addEventListener('wheel', function (e) {
            e.preventDefault();
            var delta = e.deltaY < 0 ? -0.1 : 0.1;
            scale += delta;
            if (scale < 0.1) scale = 0.1;
            $expandedImage.style.transform = `scale(${scale}) rotate(${rotation}deg) translate(${translateX}px, ${translateY}px)`;
        });

        $expandedImage.style.transform = `scale(${scale}) rotate(${rotation}deg) translate(${translateX}px, ${translateY}px)`;
        $expandedImage.style.transition = 'transform 0.2s ease-out';

        // 显示旋转和关闭按钮
        showControlButtons();
    }

    // 显示控制按钮
    function showControlButtons() {
        if (!$rotateLeftButton) {
            $rotateLeftButton = document.createElement('div');
            $rotateLeftButton.classList.add('gm-rotate-left');
            $rotateLeftButton.textContent = '⮜'; // 旋转左箭头
            document.body.appendChild($rotateLeftButton);

            $rotateLeftButton.addEventListener('click', function () {
                rotation -= 90; // 每次左旋转90度
                $expandedImage.style.transform = `scale(${scale}) rotate(${rotation}deg) translate(${translateX}px, ${translateY}px)`;
            });
        }
        if (!$rotateRightButton) {
            $rotateRightButton = document.createElement('div');
            $rotateRightButton.classList.add('gm-rotate-right');
            $rotateRightButton.textContent = '⮞'; // 旋转右箭头
            document.body.appendChild($rotateRightButton);

            $rotateRightButton.addEventListener('click', function () {
                rotation += 90; // 每次右旋转90度
                $expandedImage.style.transform = `scale(${scale}) rotate(${rotation}deg) translate(${translateX}px, ${translateY}px)`;
            });
        }
        if (!$closeButton) {
            $closeButton = document.createElement('div');
            $closeButton.classList.add('gm-close-button');
            $closeButton.textContent = '✖'; // 关闭按钮
            document.body.appendChild($closeButton);

            $closeButton.addEventListener('click', function () {
                closeEnlargedImage(document.querySelector('.gm-expanded-image-container'));
            });
        }
        if (!$prevButton) {
            $prevButton = document.createElement('div');
            $prevButton.classList.add('gm-prev-button');
            $prevButton.textContent = '⮜'; // 向前按钮
            document.body.appendChild($prevButton);

            $prevButton.addEventListener('click', function () {
                showPrevImage();
            });
        }
        if (!$nextButton) {
            $nextButton = document.createElement('div');
            $nextButton.classList.add('gm-next-button');
            $nextButton.textContent = '⮞'; // 向后按钮
            document.body.appendChild($nextButton);

            $nextButton.addEventListener('click', function () {
                showNextImage();
            });
        }
        $rotateLeftButton.style.display = 'flex';
        $rotateRightButton.style.display = 'flex';
        $closeButton.style.display = 'flex';
        $prevButton.style.display = 'flex';
        $nextButton.style.display = 'flex';
    }

    // 隐藏控制按钮
    function hideControlButtons() {
        if ($rotateLeftButton) {
            $rotateLeftButton.style.display = 'none';
        }
        if ($rotateRightButton) {
            $rotateRightButton.style.display = 'none';
        }
        if ($closeButton) {
            $closeButton.style.display = 'none';
        }
        if ($prevButton) {
            $prevButton.style.display = 'none';
        }
        if ($nextButton) {
            $nextButton.style.display = 'none';
        }
    }

    // 关闭放大图片
    function closeEnlargedImage($expandedContainer) {
        document.body.style.overflow = 'auto'; // 取消大图查看时允许滑动
        $expandedContainer.style.display = 'none';
        $expandedContainer.remove();
        hideControlButtons();
        resetTransformations();
    }

    // 重置转换属性
    function resetTransformations() {
        scale = 1;
        rotation = 0;
        translateX = 0;
        translateY = 0;
    }

    // 切换到上一张图片
    function showPrevImage() {
        if (currentIndex > 0) {
            currentIndex--;
            closeEnlargedImage(document.querySelector('.gm-expanded-image-container'));
            enlargeImage(images[currentIndex]);
        }
    }

    // 切换到下一张图片
    function showNextImage() {
        if (currentIndex < images.length - 1) {
            currentIndex++;
            closeEnlargedImage(document.querySelector('.gm-expanded-image-container'));
            enlargeImage(images[currentIndex]);
        }
    }

    // 检查当前网址是否在urlList中
    function checkCurrentUrl() {
        var currentUrl = window.location.origin;
        if (urlList.includes(currentUrl)) {
            // 如果匹配,执行操作
            disableImageClick();
        }
    }

    /**
     * 创建并显示透明提示框
     * show_message('这是一个自定义的提示框!', 'center-top');
     * show_message('这是一个自定义的提示框!', 'center-top', 3, 0.5, true, '#67C23A', '#fff');
     * @param {string} content - 提示框中的内容
     * @param {string} [position='center-top'] - 提示框的位置,值可以是 'top-right', 'top-left', 'bottom-right', 'bottom-left', 'center-top', 'center-bottom',默认值为 'center-top'
     * @param {number} [autoCloseAfter=3] - 自动关闭提示框的时间(秒),默认值为 3 秒
     * @param {number} [opacity=0.5] - 提示框的透明度,值范围从 0(完全透明)到 1(完全不透明),默认值为 0.5
     * @param {boolean} [hasCloseButton=false] - 是否显示关闭按钮,默认值为 false
     * @param {string} [backgroundColor='#000'] - 提示框的背景颜色,默认值为黑色。可使用类似 Element UI 的颜色(例如 '#409EFF')。
     * @param {string} [textColor='#fff'] - 提示框的文字颜色,默认值为白色。可使用类似 Element UI 的颜色(例如 '#fff')。
     */
    function show_message(content, position = 'center-top', autoCloseAfter = 3, opacity = 0.5, hasCloseButton = false, backgroundColor = '#000', textColor = '#fff') {
        // 创建提示框的样式
        const style = document.createElement('style');
        style.textContent = `
                .custom-notification {
                    position: fixed;
                    background-color: ${backgroundColor}; /* 背景颜色 */
                    color: ${textColor}; /* 文字颜色 */
                    padding: 10px;
                    border-radius: 5px;
                    display: none; /* 默认隐藏提示框 */
                    box-shadow: 0 2px 10px rgba(0, 0, 0, 0.5);
                    z-index: 1000; /* 确保提示框在其他内容之上 */
                    transition: opacity 0.5s ease; /* 添加过渡效果 */
                    max-width: 300px; /* 设置最大宽度 */
                    overflow: hidden; /* 隐藏溢出内容 */
                    opacity: ${opacity}; /* 透明度 */
                }
                
                .custom-notification-content {
                    display: flex;
                    align-items: center; /* 垂直居中对齐 */
                    justify-content: space-between; /* 内容和按钮在水平上分开 */
                }
                
                .custom-notification button {
                    background-color: #ff4c4c; /* 红色背景 */
                    color: #fff; /* 白色文字 */
                    border: none;
                    padding: 5px 10px;
                    border-radius: 3px;
                    cursor: pointer;
                    margin-left: 10px; /* 按钮和内容之间的间距 */
                }
                
                .custom-notification button:hover {
                    background-color: #ff0000; /* 鼠标悬停时更深的红色 */
                }
            `;
        document.head.appendChild(style);

        // 创建提示框的 HTML
        const notification = document.createElement('div');
        notification.className = 'custom-notification';
        notification.innerHTML = `
                <div class="custom-notification-content">
                    <p>${content}</p>
                    ${hasCloseButton ? '<button id="closeNotification">✖</button>' : ''}
                </div>
            `;
        document.body.appendChild(notification);

        // 根据位置参数设置提示框的位置
        switch (position) {
            case 'top-right':
                notification.style.top = '20px';
                notification.style.right = '20px';
                notification.style.bottom = '';
                notification.style.left = '';
                break;
            case 'top-left':
                notification.style.top = '20px';
                notification.style.left = '20px';
                notification.style.bottom = '';
                notification.style.right = '';
                break;
            case 'bottom-right':
                notification.style.bottom = '20px';
                notification.style.right = '20px';
                notification.style.top = '';
                notification.style.left = '';
                break;
            case 'bottom-left':
                notification.style.bottom = '20px';
                notification.style.left = '20px';
                notification.style.top = '';
                notification.style.right = '';
                break;
            case 'center-top':
                notification.style.top = '20px';
                notification.style.left = '50%';
                notification.style.transform = 'translateX(-50%)';
                notification.style.bottom = '';
                notification.style.right = '';
                break;
            case 'center-bottom':
                notification.style.bottom = '20px';
                notification.style.left = '50%';
                notification.style.transform = 'translateX(-50%)';
                notification.style.top = '';
                notification.style.right = '';
                break;
            default:
                console.warn('Unknown position:', position);
                // 默认位置为 center-top
                notification.style.top = '20px';
                notification.style.left = '50%';
                notification.style.transform = 'translateX(-50%)';
                notification.style.bottom = '';
                notification.style.right = '';
                break;
        }

        // 显示提示框
        notification.style.display = 'block';

        // 关闭提示框的函数
        function closeNotification() {
            notification.style.opacity = '0';
            setTimeout(() => {
                notification.style.display = 'none';
            }, 500); // 匹配过渡效果的时间
        }

        // 如果有关闭按钮,为其添加事件监听
        if (hasCloseButton) {
            const closeNotificationButton = document.getElementById('closeNotification');
            closeNotificationButton.addEventListener('click', closeNotification);

            // 自动关闭提示框的定时器
            setTimeout(() => {
                if (notification.style.display !== 'none') {
                    closeNotification();
                }
            }, 30000); // 30秒后自动关闭
        } else {
            // 自动关闭提示框
            setTimeout(closeNotification, autoCloseAfter * 1000); // 自动关闭时间(毫秒)
        }
    }

    // 加载所有懒加载的图片
    function loadLazyImages() {
        // 选择所有带有 data-src 属性的图片
        const lazyImages = document.querySelectorAll('img[data-src]');

        lazyImages.forEach(img => {
            const src = img.getAttribute('data-src');
            if (src) {
                img.src = src; // 将 data-src 的值赋给 src 属性
                img.removeAttribute('data-src'); // 可选:移除 data-src 属性
            }
        });
    }

    // 加载所有懒加载的背景图片
    function loadLazyBackgrounds() {
        const lazyBackgrounds = document.querySelectorAll('[data-background], [data-bg]');

        lazyBackgrounds.forEach(element => {
            const bg = element.getAttribute('data-background') || element.getAttribute('data-bg');
            if (bg) {
                element.style.backgroundImage = `url(${bg})`; // 将 data-background 或 data-bg 的值赋给 backgroundImage 样式
                element.removeAttribute('data-background'); // 可选:移除 data-background 属性
                element.removeAttribute('data-bg'); // 可选:移除 data-bg 属性
            }
        });
    }

    // 初始化懒加载
    function load_image() {
        // 加载图片和背景图片
        loadLazyImages();
        loadLazyBackgrounds();

        // 可选:观察 DOM 的变化以处理动态添加的内容
        const observer = new MutationObserver(() => {
            loadLazyImages();
            loadLazyBackgrounds();
        });

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

    // 初始化函数
    function init() {
        load_image();       //加载所有懒加载的图片
        addStyles();        //添加样式
        checkCurrentUrl();  //检查当前网址是否在urlList中
        addFloatingButton();//添加悬浮图片按钮
    }

    // 执行初始化函数
    init();
})();