图片预览

平庸与极限,究竟哪种是你的风格?随心所欲,不受拘束,此时此刻突破极限。

您需要先安裝使用者腳本管理器擴展,如 TampermonkeyGreasemonkeyViolentmonkey 之後才能安裝該腳本。

You will need to install an extension such as Tampermonkey to install this script.

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyUserscripts 後才能安裝該腳本。

你需要先安裝一款使用者腳本管理器擴展,比如 Tampermonkey,才能安裝此腳本

您需要先安裝使用者腳本管理器擴充功能後才能安裝該腳本。

(我已經安裝了使用者腳本管理器,讓我安裝!)

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

(我已經安裝了使用者樣式管理器,讓我安裝!)

// ==UserScript==
// @name         图片预览
// @version      3.2.1
// @description  平庸与极限,究竟哪种是你的风格?随心所欲,不受拘束,此时此刻突破极限。
// @author       hiisme
// @match       https://image.baidu.com/*
// @match       https://unsplash.com/*
// @match       https://www.google.com/*
// @grant        GM_registerMenuCommand
// @grant        GM_setValue
// @grant        GM_getValue
// @grant        GM_notification
// @namespace https://greasyfork.org/users/217852
// ==/UserScript==

(function() {
    'use strict';

    // Retrieve settings from storage
    let shortcut = GM_getValue('shortcut', 'Ctrl+Shift+I');
    let windowWidth = GM_getValue('windowWidth', screen.width);
    let windowHeight = GM_getValue('windowHeight', screen.height);
    let adaptiveWindowSize = GM_getValue('adaptiveWindowSize', true);
    let enableAcrylicBlur = GM_getValue('enableAcrylicBlur', true);
    let mode = GM_getValue('mode', 'Extreme');

    // Util to toggle settings
    function toggleSetting(settingKey, settingName) {
        const currentValue = GM_getValue(settingKey, true);
        const newValue = !currentValue;
        GM_setValue(settingKey, newValue);
        GM_notification({
            text: `${settingName}现已${newValue ? '启用' : '禁用'}`,
            title: '图片浏览',
            timeout: 3000
        });
    }

    function toggleAdaptiveWindowSize() {
        toggleSetting('adaptiveWindowSize', '智能自适应窗口大小');
    }

    function toggleAcrylicBlur() {
        toggleSetting('enableAcrylicBlur', 'Acrylic模糊');
    }

    function toggleMode() {
        mode = (mode === 'Extreme') ? 'Normal' : 'Extreme';
        GM_setValue('mode', mode);
        GM_notification({
            text: `模式已切换为: ${mode === 'Extreme' ? '极限模式' : '普通模式'}`,
            title: '图片浏览',
            timeout: 3000
        });
    }

    async function setShortcut() {
        const newShortcut = prompt('输入新的快捷键组合以切换模式 (如: Ctrl+Shift+I):', shortcut);
        if (newShortcut) {
            shortcut = newShortcut;
            GM_setValue('shortcut', shortcut);
            GM_notification({
                text: `快捷键已设置为: ${shortcut}`,
                title: '图片浏览',
                timeout: 3000
            });
        }
    }

    async function setWindowSize() {
        const newWidth = prompt('输入新窗口宽度 (像素):', windowWidth);
        const newHeight = prompt('输入新窗口高度 (像素):', windowHeight);
        if (newWidth && newHeight) {
            windowWidth = parseInt(newWidth, 10);
            windowHeight = parseInt(newHeight, 10);
            GM_setValue('windowWidth', windowWidth);
            GM_setValue('windowHeight', windowHeight);
            GM_notification({
                text: `窗口大小设置为: ${windowWidth}x${windowHeight}`,
                title: '图片浏览',
                timeout: 3000
            });
        }
    }

    // Register Menu Commands
    GM_registerMenuCommand("设定极限平庸的按钮", setShortcut);
    GM_registerMenuCommand("一成不变的画布大小", setWindowSize);
    GM_registerMenuCommand("自由的天地不受拘束", toggleAdaptiveWindowSize);
    GM_registerMenuCommand("模糊画布之下的风景", toggleAcrylicBlur);
    GM_registerMenuCommand("极限与平庸不可兼得", toggleMode);

    function parseShortcut(shortcut, event) {
        const keys = shortcut.toLowerCase().split('+').map(k => k.trim());
        return keys.includes(event.key.toLowerCase()) &&
               (keys.includes('ctrl') === event.ctrlKey) &&
               (keys.includes('shift') === event.shiftKey) &&
               (keys.includes('alt') === event.altKey) &&
               (keys.includes('meta') || keys.includes('cmd') || keys.includes('command') === event.metaKey);
    }

    function onKeyDown(event) {
        if (parseShortcut(shortcut, event)) {
            event.preventDefault();
            toggleMode(); // Toggle mode on shortcut
        }
    }
    document.addEventListener('keydown', onKeyDown);

    function calculateWindowSize(imageWidth, imageHeight) {
        const screenWidth = screen.width;
        const screenHeight = screen.height;

        const minWidth = screenWidth * 0.2;
        const minHeight = screenHeight * 0.2;
        const maxWidth = screenWidth * 0.9;
        const maxHeight = screenHeight * 0.9;

        const aspectRatio = imageWidth / imageHeight;

        let width = Math.max(minWidth, Math.min(imageWidth, maxWidth));
        let height = width / aspectRatio;
        if (height > maxHeight) {
            height = maxHeight;
            width = height * aspectRatio;
        }

        return { width, height };
    }

    function createLoadingSpinner() {
        const spinner = document.createElement('div');
        spinner.style.position = 'fixed';
        spinner.style.top = '50%';
        spinner.style.left = '50%';
        spinner.style.transform = 'translate(-50%, -50%)';
        spinner.style.width = '50px';
        spinner.style.height = '50px';
        spinner.style.border = '4px solid rgba(0, 0, 0, 0.1)';
        spinner.style.borderTop = '4px solid #fff';
        spinner.style.borderRadius = '50%';
        spinner.style.animation = 'spin 0.6s linear infinite';
        spinner.style.zIndex = '9999';

        const style = document.createElement('style');
        style.innerHTML = `
            @keyframes spin {
                0% { transform: rotate(0deg); }
                100% { transform: rotate(360deg); }
            }
        `;
        document.head.appendChild(style);
        return spinner;
    }

    function createImageViewer(url) {
        const spinner = createLoadingSpinner();
        document.body.appendChild(spinner);

        const img = new Image();
        img.src = url;

        img.onload = function() {
            let newWindowWidth = windowWidth;
            let newWindowHeight = windowHeight;

            if (adaptiveWindowSize) {
                const size = calculateWindowSize(img.width, img.height);
                newWindowWidth = size.width;
                newWindowHeight = size.height;
            }

            const leftOffset = (screen.width - newWindowWidth) / 2;
            const topPosition = (screen.height - newWindowHeight) / 2 - 40;

            const newWindow = window.open('', 'Image Preview', `width=${newWindowWidth},height=${newWindowHeight},top=${topPosition},left=${leftOffset},resizable=yes,scrollbars=no`);

            if (newWindow) {
                newWindow.document.title = '图片预览';

                // Apply modern CSS layout and acrylic blur
                newWindow.document.open();
                newWindow.document.write(`
                    <html>
                    <head>
                        <title>图片预览</title>
                        <style>
                            body {
                                margin: 0;
                                overflow: hidden;
                                display: flex;
                                justify-content: center;
                                align-items: center;
                                background-color: black;
                                height: 100vh;
                                position: relative;
                            }
                            ${enableAcrylicBlur ? `
                            .background {
                                position: absolute;
                                top: 0;
                                left: 0;
                                width: 100%;
                                height: 100%;
                                background: url('${url}') center/cover no-repeat;
                                filter: blur(20px);
                                z-index: -2;
                            }
                            .overlay {
                                position: absolute;
                                top: 0;
                                left: 0;
                                width: 100%;
                                height: 100%;
                                backdrop-filter: blur(20px) saturate(150%);
                                background-color: rgba(255, 255, 255, 0.3);
                                z-index: -1;
                            }` : ''}
                            img {
                                max-width: 100%;
                                max-height: 100%;
                                cursor: grab;
                                transition: transform 0.1s ease;
                                position: relative;
                                user-select: none;
                                z-index: 1;
                            }
                        </style>
                        <meta name="theme-color" content="#000000">
                    </head>
                    <body>
                        ${enableAcrylicBlur ? `<div class="background"></div><div class="overlay"></div>` : ''}
                        <img src="${url}" alt="图片" />
                    </body>
                    </html>
                `);
                newWindow.document.close();

                // Remove the spinner after the image is displayed
                document.body.removeChild(spinner);

                let scale = 1;
                let imgX = 0, imgY = 0;
                let isDragging = false;
                let lastMouseX = 0, lastMouseY = 0;

                const imgElement = newWindow.document.querySelector('img');

                function onMouseMove(event) {
                    if (isDragging) {
                        const deltaX = event.clientX - lastMouseX;
                        const deltaY = event.clientY - lastMouseY;
                        imgX += deltaX;
                        imgY += deltaY;
                        imgElement.style.transform = `translate(${imgX}px, ${imgY}px) scale(${scale})`;
                        lastMouseX = event.clientX;
                        lastMouseY = event.clientY;
                    }
                }

                function onMouseUp() {
                    isDragging = false;
                    imgElement.style.cursor = 'grab';
                    newWindow.removeEventListener('mousemove', onMouseMove);
                    newWindow.removeEventListener('mouseup', onMouseUp);
                }

                function onWheel(event) {
                    event.preventDefault();
                    const scaleAmount = event.deltaY > 0 ? 0.9 : 1.1;
                    scale *= scaleAmount;
                    imgElement.style.transform = `translate(${imgX}px, ${imgY}px) scale(${scale})`;
                }

                function onMouseDown(event) {
                    event.preventDefault();
                    isDragging = true;
                    imgElement.style.cursor = 'grabbing';
                    lastMouseX = event.clientX;
                    lastMouseY = event.clientY;
                    newWindow.addEventListener('mousemove', onMouseMove);
                    newWindow.addEventListener('mouseup', onMouseUp);
                }

                function onDoubleClick() {
                    newWindow.close();
                }

                imgElement.addEventListener('wheel', onWheel, { passive: false });
                imgElement.addEventListener('mousedown', onMouseDown);
                imgElement.addEventListener('dblclick', onDoubleClick);

                newWindow.addEventListener('beforeunload', () => {
                    imgElement.removeEventListener('wheel', onWheel);
                    imgElement.removeEventListener('mousedown', onMouseDown);
                    imgElement.removeEventListener('dblclick', onDoubleClick);
                    newWindow.removeEventListener('mousemove', onMouseMove);
                    newWindow.removeEventListener('mouseup', onMouseUp);
                });
            } else {
                // Remove the spinner if the window failed to open
                document.body.removeChild(spinner);
            }
        };

        img.onerror = function() {
            // Remove the spinner if the image failed to load
            document.body.removeChild(spinner);
            GM_notification({
                text: '图片加载失败',
                title: '图片浏览',
                timeout: 3000
            });
        };
    }

    // Optimized Intersection Observer for preloading images
    function preloadImage(element) {
        const observer = new IntersectionObserver((entries, obs) => {
            entries.forEach(entry => {
                if (entry.isIntersecting) {
                    createImageViewer(entry.target.src || entry.target.href);
                    obs.unobserve(entry.target);
                }
            });
        }, {
            rootMargin: '50px',
            threshold: 0.1
        });

        observer.observe(element);
    }

    function onClick(event) {
        const target = event.target;

        if (event.shiftKey) {
            return; // If Shift is pressed, use the default behavior
        }

        if (mode === 'Extreme') {
            // Extreme Mode: Handle images and links aggressively
            if (target.tagName === 'A' && /\.(jpg|jpeg|png|gif|webp)$/i.test(target.href)) {
                event.preventDefault();
                event.stopPropagation();
                preloadImage(target);
            } else if (target.tagName === 'IMG' && /\.(jpg|jpeg|png|gif|webp)$/i.test(target.src)) {
                event.preventDefault();
                event.stopPropagation();
                preloadImage(target);
            } else if (target.tagName === 'IMG' && target.src) {
                event.preventDefault();
                event.stopPropagation();
                preloadImage(target);
            }
        } else {
            // Normal Mode: Handle only image elements and image links
            if (target.tagName === 'IMG' && /\.(jpg|jpeg|png|gif|webp)$/i.test(target.src)) {
                event.preventDefault();
                event.stopPropagation();
                preloadImage(target);
            } else if (target.tagName === 'A' && /\.(jpg|jpeg|png|gif|webp)$/i.test(target.href)) {
                event.preventDefault();
                event.stopPropagation();
                preloadImage(target);
            }
        }
    }

    document.addEventListener('click', onClick, true);

    function cleanUp() {
        document.removeEventListener('keydown', onKeyDown);
        document.removeEventListener('click', onClick, true);
    }

    window.addEventListener('unload', cleanUp);
})();