Hover Zoom Minus --

popup and hover zoom pan scroll any image on any website

当前为 2024-03-13 提交的版本,查看 最新版本

您需要先安装一个扩展,例如 篡改猴Greasemonkey暴力猴,之后才能安装此脚本。

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

您需要先安装一个扩展,例如 篡改猴暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴Userscripts ,之后才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。

您需要先安装用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name             Hover Zoom Minus --
// @namespace        Hover Zoom Minus --
// @version          1.0
// @description      popup and hover zoom pan scroll any image on any website
// @author           Ein, with help from Copilot AI
// @match            *://*/*
// @license          MIT
// @grant            none
// ==/UserScript==

// 1. to use hover over the image(container) to view a popup of the target image
// 2. to zoom in/out use wheel up/down. to reset press the scroll button
// 3. click left mouse to lock popup this will follow the mouse, click again to release
// 4. while locked the wheel up/down will act as scroll up/down
// 5. to turn on/off hover at the bottom of the page
// 6. easy to configure

(function() {
    'use strict';

    // Configuration ----------------------------------------------------------

    // Define your list of sites you want HoverZoomMinus to run with,
    // 1st array value - 1 will default it to turn on, 0 defaults it to turn off
    // 2nd array value - "center" to always spawn it in center otherwise '' [blank] spawns near the mouse
    // 3rd array value - allowed interval between respawing of popup. ie when exited on popup but imedietly touches an img container thus making it blinks or unsuable

    const siteList = {
        /*---- reddit */ 'new.reddit.com':     [1, 'center','0'],
        /*------ 9gag */ '9gag.com':           [1, 'center', '100'],
        /*---- feedly */ 'feedly.com':         [1, 'center', '200'],
        /*----- 4chan */ 'boards.4chan.org':   [1, '', '400'],
        /* deviantart */ 'www.deviantart.com': [0, 'center', '300'],
    };

    // image container [hover area that triggers the popup]
    const imgContainers = `
        /* --- reddit */ ._3Oa0THmZ3f5iZXAQ0hBJ0k > div, ._35oEP5zLnhKEbj5BlkTBUA, ._1ti9kvv_PMZEF2phzAjsGW > div, ._28TEYBuEdOuE3kN6UyoKMa div, ._3Oa0THmZ3f5iZXAQ0hBJ0k.WjuR4W-BBrvdtABBeKUMx div,
        /* ----- 9gag */ .post-container .post-view > picture,
        /* --- feedly */ .PinableImageContainer, .entryBody,
        /* ---- 4chan */ div.post div.file a,
        /* deviantart */ ._3_LJY, ._2e1g3, ._2SlAD
    `;
    // target img
    const imgElements = `
        /* --- reddit */ ._2_tDEnGMLxpM6uOa2kaDB3, ._1dwExqTGJH2jnA-MYGkEL-,
        /* ----- 9gag */ .post-container .post-view > picture > img,
        /* --- feedly */ .pinable, .entryBody img,
        /* ---- 4chan */ div.post div.file img:nth-child(2), div.post div.file img:nth-child(1), ._3Oa0THmZ3f5iZXAQ0hBJ0k.WjuR4W-BBrvdtABBeKUMx img,
        /* deviantart */ ._3_LJY img._2e1g3 img, ._2SlAD img
    `;
    // excluded element
    const nopeElements = `
    /* ------- reddit */ ._2ED-O3JtIcOqp8iIL1G5cg
    `;

    //-------------------------------------------------------------------------


    // The HoverZoomMinus Function---------------------------------------------
    function HoverZoomMinus() {

        // Style for the popup image
        const style = document.createElement('style');
        style.type = 'text/css';
        style.innerHTML = `
        .popup-image {
        max-height: calc(90vh - 10px);
        z-index: 1000;
        display: none;
        cursor: move;}`;
        document.head.appendChild(style);

        // Creating the popup image and backdrop
        const popup = document.createElement('img');
        popup.className = 'popup-image';
        document.body.appendChild(popup);
        const backdrop = document.createElement('div');
        backdrop.className = 'popup-backdrop';
        backdrop.style.cssText = `
        position: fixed;
        top: 0;
        left: 0;
        width: 100 vw;
        height: 100 vh;
        display: none;`;
        document.body.appendChild(backdrop);

        //-------------------------------------------------------------------------


        // Function to show the popup
        function showPopup(src, mouseX, mouseY) {
            popup.src = src;
            popup.style.display = 'block';
            popup.style.position = 'fixed';
            popup.style.transform = 'translate(-50%, -50%)';
            backdrop.style.display = 'block';
            backdrop.style.zIndex = '999';
            backdrop.style.backdropFilter = 'blur(10px)';
            const currentDomain = window.location.hostname;
            const position = siteList[currentDomain] ? siteList[currentDomain][1] : '';
            if (position === 'center') {
                popup.style.top = '50%';
                popup.style.left = '50%';
            } else {
                popup.style.top = `${mouseY}px`;
                popup.style.left = `${mouseX}px`;
            }
        }

        //-------------------------------------------------------------------------


        // Function combined Zoom and pan
        let isPanning = false;
        let offsetX, offsetY;
        let scale = 1;
        const ZOOM_SPEED = 0.005;


        popup.addEventListener('click', function(event) {
            isPanning = !isPanning;
            if (isPanning) {
                let rect = popup.getBoundingClientRect();
                offsetX = event.clientX - rect.left - (rect.width / 2);
                offsetY = event.clientY - rect.top - (rect.height / 2);
            }
        });

        document.addEventListener('mousemove', function(event) {
            if (isPanning) {
                popup.style.left = (event.clientX - offsetX) + 'px';
                popup.style.top = (event.clientY - offsetY) + 'px';
            }
        });

        function zoomOrPan(event) {
            event.preventDefault();
            if (isPanning) {
                let deltaY = event.deltaY * -ZOOM_SPEED;
                let newTop = parseInt(popup.style.top) || 0;
                newTop += deltaY * 100;
                popup.style.top = newTop + 'px';

                offsetY -= deltaY * 100;
            } else {
                scale += event.deltaY * -ZOOM_SPEED;
                scale = Math.min(Math.max(0.125, scale), 4);
                popup.style.transform = `translate(-50%, -50%) scale(${scale})`;
            }
        }

        popup.addEventListener('wheel', zoomOrPan);

        //-------------------------------------------------------------------------


        // Function to show popup
        let popupTimer;
        document.addEventListener('mouseover', function(e) {

            if (popupTimer) return;

            let target = e.target.closest(imgContainers);
            if (!target) return;
            if (target.querySelector(nopeElements)) return;

            let currentContainer = target;
            const imageElement = currentContainer.querySelector(imgElements);
            if (imageElement) {
                const currentDomain = window.location.hostname;
                const domainConfig = siteList[currentDomain];
                const position = siteList[currentDomain] ? siteList[currentDomain][1] : '';
                const delayTimer = domainConfig ? domainConfig[2] : '';

                if (delayTimer === '') {
                    if (position === 'center') {
                        showPopup(imageElement.src);
                    } else {
                        showPopup(imageElement.src, e.clientX, e.clientY);
                    }
                } else {
                    popupTimer = setTimeout(() => {
                        if (position === 'center') {
                            showPopup(imageElement.src);
                        } else {
                            showPopup(imageElement.src, e.clientX, e.clientY);
                        }
                        popupTimer = null;
                    }, parseInt(delayTimer));
                }
            }
        });

        //-------------------------------------------------------------------------


        // Function to hide popup

        function hidePopup() {
            if (popupTimer) {
                clearTimeout(popupTimer);
            }
            document.body.appendChild(backdrop);
            popup.style.display = 'none';
            popup.style.left = '50%';
            popup.style.top = '50%';
            popup.style.position = 'fixed';
            popup.style.transform = 'translate(-50%, -50%) scale(1)';
            backdrop.style.zIndex = '';
            backdrop.style.display = 'none';
            backdrop.style.backdropFilter = '';
            isPanning = false;
        }

        document.addEventListener('mouseout', function(e) {
            let target = e.target.closest('.imgContainers');
            let relatedTarget = e.relatedTarget;

            if (target && (!relatedTarget || (!relatedTarget.matches('.popup-image') && !relatedTarget.closest('.imgContainers')))) {
                hidePopup();
                const currentDomain = window.location.hostname;
                const delayTimer = siteList[currentDomain] ? siteList[currentDomain][2] : '';

                if (delayTimer !== '') {
                    popupTimer = setTimeout(() => {
                        popupTimer = null;
                    }, parseInt(delayTimer));
                }
            }
        });

        popup.addEventListener('mouseout', function() {
            hidePopup();
            const currentDomain = window.location.hostname;
            const delayTimer = siteList[currentDomain] ? siteList[currentDomain][2] : '';

            if (delayTimer !== '') {
                popupTimer = setTimeout(() => {
                    popupTimer = null;
                }, parseInt(delayTimer));
            }
        });

        document.addEventListener('keydown', function(event) {
            if (event.key === "Escape") {
                event.preventDefault();
                hidePopup();
            }
        });
    }

    //-------------------------------------------------------------------------


    // Is HoverZoomMinus to be run
    const indicatorBar = document.createElement('div');
    indicatorBar.style.cssText = `
    position: fixed;
    bottom: 0;
    left: 50%;
    transform: translateX(-50%);
    z-index: 9999;
    height: 30px;
    width: 50vw;
    background: #0000;`;
    document.body.appendChild(indicatorBar);

    function toggleIndicator() {
        indicatorValue = 1 - indicatorValue;
        indicatorBar.style.background = indicatorValue ? 'rgba(50, 190, 152, 0.5)' : 'rgba(174, 0, 1, 0.5)';
        setTimeout(() => {
            indicatorBar.style.background = '#0000';
        }, 1000);
        if (indicatorValue === 1) {
            HoverZoomMinus();
        } else {
            const existingPopup = document.body.querySelector('.popup-image');
            if (existingPopup) document.body.removeChild(existingPopup);
            const existingBackdrop = document.body.querySelector('.popup-backdrop');
            if (existingBackdrop) document.body.removeChild(existingBackdrop);
        }
    }

    let indicatorValue;
    const currentDomain = window.location.hostname;
    if (siteList.hasOwnProperty(currentDomain)) {
        indicatorValue = siteList[currentDomain][0];

        let hoverTimeout;
        indicatorBar.addEventListener('mouseenter', () => {
            hoverTimeout = setTimeout(toggleIndicator, 500);
        });
        indicatorBar.addEventListener('mouseleave', () => {
            clearTimeout(hoverTimeout);
            indicatorBar.style.background = '#0000';
        });
        if (indicatorValue === 1) {
            HoverZoomMinus();
        }

    } else { return; }

})();