图片预览

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

// ==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);
})();