原生图片浏览器

最优雅的图片打开方式

Stan na 28-08-2024. Zobacz najnowsza wersja.

// ==UserScript==
// @name         原生图片浏览器
// @version      2.2
// @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';

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

    async function toggleImageClick() {
        enableImageClick = !enableImageClick;
        await GM_setValue('enableImageClick', enableImageClick);
        GM_notification({
            text: `Image click-to-open feature is now ${enableImageClick ? 'enabled' : 'disabled'}.`,
            title: 'Image Viewer',
            timeout: 3000
        });
    }

    async function setShortcut() {
        const newShortcut = prompt('Enter new shortcut (e.g., Ctrl+Shift+I):', shortcut);
        if (newShortcut) {
            shortcut = newShortcut;
            await GM_setValue('shortcut', shortcut);
            GM_notification({
                text: `Shortcut set to: ${shortcut}`,
                title: 'Image Viewer',
                timeout: 3000
            });
        }
    }

    async function setWindowSize() {
        const newWidth = prompt('Enter new window width (pixels):', windowWidth);
        const newHeight = prompt('Enter new window height (pixels):', 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: `Window size set to: ${windowWidth}x${windowHeight}`,
                title: 'Image Viewer',
                timeout: 3000
            });
        }
    }

    GM_registerMenuCommand("Toggle Image Click-to-Open", toggleImageClick);
    GM_registerMenuCommand("Set Shortcut", setShortcut);
    GM_registerMenuCommand("Set Window Size", setWindowSize);

    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 createImageViewer(url) {
        const topOffset = screen.height * 0.05;
        const leftOffset = (screen.width - windowWidth) / 2;
        const topPosition = (screen.height - windowHeight) / 2 - topOffset;

        const newWindow = window.open('', '_blank', `width=${windowWidth},height=${windowHeight},top=${topPosition},left=${leftOffset},resizable=yes,scrollbars=no`);

        if (newWindow) {
            // Set a minimal HTML structure with inline styles to prevent flicker
            newWindow.document.open();
            newWindow.document.write(`
                <html>
                <head>
                    <style>
                        body {
                            margin: 0;
                            overflow: hidden;
                            display: flex;
                            justify-content: center;
                            align-items: center;
                            background: black;
                        }
                        img {
                            max-width: 100vw;
                            max-height: 100vh;
                            cursor: grab;
                            transition: transform 0.1s ease;
                            position: absolute;
                            user-drag: none;
                            -webkit-user-drag: none;
                        }
                    </style>
                    <meta name="theme-color" content="#000000">
                </head>
                <body>
                    <img src="${url}" alt="Image" />
                </body>
                </html>
            `);
            newWindow.document.close();

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

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

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

            function onWheelHandler(event) {
                event.preventDefault();
                const scaleAmount = event.deltaY > 0 ? 0.9 : 1.1;
                scale *= scaleAmount;
                newWindow.document.querySelector('img').style.transform = `translate(${imgX}px, ${imgY}px) scale(${scale})`;
            }

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

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

            newWindow.document.body.addEventListener('wheel', onWheelHandler, { passive: false });
            newWindow.document.body.addEventListener('mousedown', onMouseDownHandler);
            newWindow.document.body.addEventListener('dblclick', onDoubleClickHandler);

            newWindow.addEventListener('beforeunload', () => {
                newWindow.document.body.removeEventListener('wheel', onWheelHandler);
                newWindow.document.body.removeEventListener('mousedown', onMouseDownHandler);
                newWindow.document.body.removeEventListener('dblclick', onDoubleClickHandler);
                newWindow.removeEventListener('mousemove', onMouseMove);
                newWindow.removeEventListener('mouseup', onMouseUp);
            });
        }
    }

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

        if (target.tagName === 'A' && /\.(jpg|jpeg|png|gif|webp)$/i.test(target.href)) {
            event.preventDefault();
            event.stopPropagation(); // Prevent default behavior and stop further event propagation
            createImageViewer(target.href);
        } else if (enableImageClick && target.tagName === 'IMG' && /\.(jpg|jpeg|png|gif|webp)$/i.test(target.src)) {
            event.preventDefault();
            event.stopPropagation(); // Prevent default behavior and stop further event propagation
            createImageViewer(target.src);
        } else if (enableImageClick && target.tagName === 'IMG' && target.src) {
            event.preventDefault();
            event.stopPropagation(); // Prevent default behavior and stop further event propagation
            createImageViewer(target.src);
        }
    }

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

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

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