纯净视界 - 原生级图片探索器

探索纯净之美,尽享智能体验: "纯净视界"是一款专为图片爱好者设计的用户脚本,它不仅呈现图片的原生之美,更通过一系列智能功能,为您带来前所未有的浏览体验

目前为 2024-08-29 提交的版本。查看 最新版本

// ==UserScript==
// @name         纯净视界 - 原生级图片探索器
// @version      3.0
// @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==

(async function() {
    'use strict';

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

    const shiftRequiredSites = ['www.pinterest.com', 'www.douyin.com', 'm.weibo.cn'];

    function shouldRequireShift() {
        return shiftRequiredSites.includes(window.location.hostname);
    }

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

    function toggleImageClick() {
        toggleSetting('enableImageClick', '点击图片开启功能');
    }

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

    function toggleAcrylicBlur() {
        toggleSetting('enableAcrylicBlur', '丙烯酸模糊背景');
    }

    async function setShortcut() {
        const newShortcut = prompt('输入新的快捷键组合 (如: Ctrl+Shift+I):', shortcut);
        if (newShortcut) {
            shortcut = newShortcut;
            await 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);
            await GM_setValue('windowWidth', windowWidth);
            await GM_setValue('windowHeight', windowHeight);
            GM_notification({
                text: `窗口大小设置为: ${windowWidth}x${windowHeight}`,
                title: '图片浏览',
                timeout: 3000
            });
        }
    }

    GM_registerMenuCommand("切换图片点击开启功能", toggleImageClick);
    GM_registerMenuCommand("设置快捷键", setShortcut);
    GM_registerMenuCommand("设置窗口大小", setWindowSize);
    GM_registerMenuCommand("切换智能自适应窗口大小", toggleAdaptiveWindowSize);
    GM_registerMenuCommand("切换丙烯酸模糊背景", toggleAcrylicBlur);

    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();
            toggleImageClick();
        }
    }
    document.addEventListener('keydown', onKeyDown);

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

        const minWidth = screenWidth * 0.3;
        const minHeight = screenHeight * 0.3;
        const maxWidth = screenWidth * 0.7;
        const maxHeight = screenHeight * 0.7;

        const aspectRatio = imageWidth / imageHeight;

        let width = imageWidth;
        let height = imageHeight;

        if (width > maxWidth) {
            width = maxWidth;
            height = width / aspectRatio;
        }
        if (height > maxHeight) {
            height = maxHeight;
            width = height * aspectRatio;
        }
        if (width < minWidth) {
            width = minWidth;
            height = width / aspectRatio;
        }
        if (height < minHeight) {
            height = minHeight;
            width = height * aspectRatio;
        }

        return { width, height };
    }

    function createImageViewer(url) {
        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();

                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);
                });
            }
        };
    }

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

        if (target.tagName === 'A' && /\.(jpg|jpeg|png|gif|webp)$/i.test(target.href)) {
            event.preventDefault();
            event.stopPropagation();
            createImageViewer(target.href);
        } else if (enableImageClick && target.tagName === 'IMG' && /\.(jpg|jpeg|png|gif|webp)$/i.test(target.src)) {
            if (!shouldRequireShift() || (shouldRequireShift() && event.shiftKey)) {
                event.preventDefault();
                event.stopPropagation();
                createImageViewer(target.src);
            }
        } else if (enableImageClick && target.tagName === 'IMG' && target.src) {
            if (!shouldRequireShift() || (shouldRequireShift() && event.shiftKey)) {
                event.preventDefault();
                event.stopPropagation();
                createImageViewer(target.src);
            }
        }
    }
    document.addEventListener('click', onClick, true);

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

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