您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
image popup: zoom, pan, scroll, pin, scale
当前为
- // ==UserScript==
- // @name Hover Zoom Minus --
- // @namespace Hover Zoom Minus --
- // @version 1.0.4
- // @description image popup: zoom, pan, scroll, pin, scale
- // @author Ein, 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 make it move along with the mouse, click again to release (indicated by green border)
- // 4. while being locked"Y" the wheel up/down will act as scroll up/down
- // 5. double click will lock it on screen preventing it from being hidden
- // 6. hover below the image and click blue bar this will make a 3rd mode for wheel bottom, which will scroll next/previous image under an album
- // 7. while locked at screen (indicated by red outline) a single click with the blurred background will unblur it, only one popup per time, so the locked popup will prevent other popup to spawn
- // 8. double clicking on blurred background will de-spawn popup
- // 9. click on top right corner to scale image
- // 10. to turn on/off hover at the bottom of the page
- (function() {
- 'use strict';
- // Configuration ----------------------------------------------------------
- // Define regexp of web page you want HoverZoomMinus to run with,
- // 1st array value - default status at start of page: '1' for on, '0' for off
- // 2nd array value - spawn position for popup: 'center' for center of screen, '' for cursor position
- // 3rd array value - allowed interval for spawning popup; i.e. when exited on popup but immediately touches an img container thus making it "blink spawn blink spawn", experiment it with the right number
- const siteConfig = {
- 'reddit.com*': [1, 'center', '0'],
- '9gag.com*': [1, 'center', '0'],
- 'feedly.com*': [1, 'center', '200'],
- '4chan.org*': [1, '', '400'],
- 'deviantart.com*': [0, 'center', '300']
- };
- // image container [hover box where popup triggers]
- const imgContainers = `
- /* ------- reddit */ ._3Oa0THmZ3f5iZXAQ0hBJ0k > div, ._35oEP5zLnhKEbj5BlkTBUA, ._1ti9kvv_PMZEF2phzAjsGW > div, ._28TEYBuEdOuE3kN6UyoKMa div, ._3Oa0THmZ3f5iZXAQ0hBJ0k.WjuR4W-BBrvdtABBeKUMx div, ._3m20hIKOhTTeMgPnfMbVNN,
- /* --------- 9gag */ .post-container .post-view > picture,
- /* ------- feedly */ .PinableImageContainer, .entryBody,
- /* -------- 4chan */ div.post div.file a,
- /* --- deviantart */ ._3_LJY, ._2e1g3, ._2SlAD, ._1R2x6
- `;
- // target img
- const imgElements = `
- /* ------- reddit */ ._2_tDEnGMLxpM6uOa2kaDB3, ._1dwExqTGJH2jnA-MYGkEL-, ._2_tDEnGMLxpM6uOa2kaDB3._1XWObl-3b9tPy64oaG6fax,
- /* --------- 9gag */ .post-container .post-view > picture > img,
- /* ------- feedly */ .pinable, .entryBody img,
- /* -------- 4chan */ div.post div.file img:nth-child(1), ._3Oa0THmZ3f5iZXAQ0hBJ0k.WjuR4W-BBrvdtABBeKUMx img, div.post div.file .fileThumb img,
- /* --- deviantart */ ._3_LJY img, ._2e1g3 img, ._2SlAD img, ._1R2x6 img
- `;
- // excluded element
- const nopeElements = `
- /* ------- reddit */ ._2ED-O3JtIcOqp8iIL1G5cg
- `;
- // AlbumSelector take note that it will only load image those that are already on the DOM tree
- // example reddit will not include all until you press tha navigator. so most of the time this will only load a few ones
- // unless you update it via interacting with reddit's navigator button or maybe you could make a special function for "specialElements" so it will load and include all image in the DOM tree
- let albumSelector = [
- /* ------- reddit */ { imgElement: '._1dwExqTGJH2jnA-MYGkEL-', albumElements: '._1apobczT0TzIKMWpza0OhL' },
- /* ------- sample */ { imgElement: 'imgElementSelector2', albumElements: 'albumSelector2' },
- ];
- // specialElements were if targeted, will call it's paired function
- const specialElements = [
- /* -------- 4chan */ { selector: 'div.post div.file .fileThumb img', func: SP1 }
- ];
- function SP1(imageElement) {
- let src = imageElement.getAttribute('src');
- if (src && src.includes('s.jpg')) {
- let newSrc = src.replace('s.jpg', '.jpg');
- imageElement.setAttribute('src', newSrc);
- }
- }
- //-------------------------------------------------------------------------
- // Variables
- const currentHref = window.location.href;
- let enableP, positionP, intervalP, URLmatched;
- Object.keys(siteConfig).some((config) => {
- const regex = new RegExp(config);
- if (currentHref.match(regex)) {
- [enableP, positionP, intervalP] = siteConfig[config];
- URLmatched = true;
- return true;
- }
- });
- // The HoverZoomMinus Function---------------------------------------------
- function HoverZoomMinus() {
- let isshowPopupEnabled = true;
- isshowPopupEnabled = true;
- const style = document.createElement('style');
- style.type = 'text/css';
- style.innerHTML = `
- .popup-container {
- display: none;
- z-index: 1001;
- cursor: move;
- }
- .popup-image {
- max-height: calc(90vh - 10px);
- display: none;
- }
- .popup-backdrop {
- position: fixed;
- top: 0;
- left: 0;
- width: 100vw;
- height: 100vh;
- display: none;
- z-index: 1000;
- }
- .LockedX {
- height: 40px;
- width: calc(100%);
- background: #0000;
- position: absolute;
- bottom: 0;
- z-index: 9999;
- }
- .scaleTR {
- height: 40px;
- width: 40px;
- background: #0000;
- position: absolute;
- top: 0;
- right: 0;
- z-index: 9999;
- }
- `;
- document.head.appendChild(style);
- const backdrop = document.createElement('div');
- backdrop.className = 'popup-backdrop';
- document.body.appendChild(backdrop);
- const popupContainer = document.createElement('div');
- popupContainer.className = 'popup-container';
- document.body.appendChild(popupContainer);
- const popup = document.createElement('img');
- popup.className = 'popup-image';
- popupContainer.appendChild(popup);
- const LockedX = document.createElement('div');
- LockedX.className = 'LockedX';
- popupContainer.appendChild(LockedX);
- const scaleTR = document.createElement('div');
- scaleTR.className = 'scaleTR';
- popupContainer.appendChild(scaleTR);
- //-------------------------------------------------------------------------
- // Zoom, Pan, Scroll
- let isLockedY = false;
- let isLockedX = false;
- let offsetX, offsetY, rectT, rectB, rectH, rectR, rectL, rectW;
- let scale = 1;
- const ZOOM_SPEED = 0.005;
- let clickTimeout;
- let popupTimer;
- popupContainer.addEventListener('click', function(event) {
- if (clickTimeout) clearTimeout(clickTimeout);
- clickTimeout = setTimeout(function() {
- if (event.target.matches('.LockedX') || event.target.closest('.LockedX') || event.target.matches('.scaleTR') || event.target.closest('.scaleTR')) {
- event.stopPropagation();
- } else {
- isLockedY = !isLockedY;
- isscaleTR = false;
- if (isLockedY) {
- popupContainer.style.outline = ishidePopupEnabled ? '' : '3px solid #ae0001';
- isLockedX = false;
- popupContainer.style.borderTop = '';
- popupContainer.style.borderBottom = '';
- popupContainer.style.borderLeft = '3px solid #00ff00';
- popupContainer.style.borderRight = '3px solid #00ff00';
- let rect = popupContainer.getBoundingClientRect();
- offsetX = event.clientX - rect.left - (rect.width / 2);
- offsetY = event.clientY - rect.top - (rect.height / 2);
- } else {
- popupContainer.style.border = '';
- }
- }}, 300);
- });
- // Scale
- let isscaleTR = false;
- scaleTR.addEventListener('click', function(event) {
- ishidePopupEnabled = false;
- isscaleTR = !isscaleTR;
- if (isscaleTR) {
- isLockedY = false;
- let rect = scaleTR.getBoundingClientRect();
- offsetX = event.clientX - rect.left - (rect.width / 2);
- offsetY = event.clientY - rect.top - (rect.height / 2);
- rectT = rect.top;
- rectB = rect.bottom;
- rectH = rect.height;
- rectW = rect.width;
- rectL = rect.left;
- rectR = rect.right;
- } else {
- scaleTR.style.border = '';
- }
- });
- let scaletrY, scaletrX;
- scaleTR.addEventListener('mouseenter', function() {
- scaleTR.style.height = '100%';
- scaleTR.style.width = '100%';
- scaleTR.style.borderTop = '3px solid #ff00ff';
- scaleTR.style.borderRight = '3px solid #ff00ff';
- });
- scaleTR.addEventListener('mouseleave', function(event) {
- scaleTR.style.height = '40px';
- scaleTR.style.width = '40px';
- scaleTR.style.border = '';
- });
- document.addEventListener('mousemove', function(event) {
- if (isLockedY) {
- popupContainer.style.left = (event.clientX - offsetX) + 'px';
- popupContainer.style.top = (event.clientY - offsetY) + 'px';
- } else if (isscaleTR) {
- scaletrY = ((rectT + rectH - event.clientY) / (rectH));
- scaletrX = ((event.clientX - rectL) / (rectW));
- popupContainer.style.transform = `translate(-50%, -50%) scale(${scaletrX}, ${scaletrY})`
- }
- });
- let imgElementsList = [];
- function ZoomOrScroll(event) {
- event.preventDefault();
- if (isLockedY) {
- let deltaY = event.deltaY * -ZOOM_SPEED;
- let newTop = parseInt(popupContainer.style.top) || 0;
- newTop += deltaY * 100;
- popupContainer.style.top = newTop + 'px';
- offsetY -= deltaY * 100;
- } else if (isLockedX && currentZeroImgElement) {
- let pair = albumSelector.find(pair => currentZeroImgElement.matches(pair.imgElement));
- if (pair) {
- let ancestorElement = currentZeroImgElement.closest(pair.albumElements);
- if (ancestorElement) {
- imgElementsList = Array.from(ancestorElement.querySelectorAll(pair.imgElement));
- let zeroIndex = imgElementsList.indexOf(currentZeroImgElement);
- let direction = event.deltaY > 0 ? 1 : -1;
- let newIndex = (zeroIndex + direction + imgElementsList.length) % imgElementsList.length;
- currentZeroImgElement = imgElementsList[newIndex];
- popup.src = currentZeroImgElement.src;
- }
- }
- } else {
- scale += event.deltaY * -ZOOM_SPEED;
- scale = Math.min(Math.max(0.125, scale), 10);
- popupContainer.style.transform = `translate(-50%, -50%) scale(${scale})`;
- }
- }
- popupContainer.addEventListener('wheel', ZoomOrScroll);
- //-------------------------------------------------------------------------
- // show popup
- function showPopup(src, mouseX, mouseY) {
- if (!isshowPopupEnabled) return;
- popup.src = src;
- popup.style.display = 'block';
- popupContainer.style.display = 'block';
- popupContainer.style.position = 'fixed';
- popupContainer.style.transform = 'translate(-50%, -50%)';
- backdrop.style.display = 'block';
- backdrop.style.zIndex = '999';
- backdrop.style.backdropFilter = 'blur(10px)';
- if (positionP === 'center') {
- popupContainer.style.top = '50%';
- popupContainer.style.left = '50%';
- } else {
- popupContainer.style.top = `${mouseY}px`;
- popupContainer.style.left = `${mouseX}px`;
- }
- }
- let currentZeroImgElement;
- document.addEventListener('mouseover', function(e) {
- if (popupTimer) return;
- let target = e.target.closest(imgContainers);
- if (target.querySelector(nopeElements)) return;
- const imageElement = target.querySelector(imgElements);
- specialElements.forEach(pair => {
- if (imageElement.matches(pair.selector)) {
- pair.func(imageElement);
- }
- });
- if (imageElement) {
- currentZeroImgElement = imageElement;
- if (intervalP === '') {
- showPopup(imageElement.src, e.clientX, e.clientY);
- } else {
- popupTimer = setTimeout(() => {
- showPopup(imageElement.src, e.clientX, e.clientY);
- popupTimer = null;
- }, parseInt(intervalP));
- }
- }
- });
- //-------------------------------------------------------------------------
- // hide popup
- function hidePopup() {
- if (!ishidePopupEnabled) return;
- imgElementsList = [];
- isshowPopupEnabled = true;
- if (popupTimer) {
- clearTimeout(popupTimer);
- }
- document.body.appendChild(backdrop);
- popup.style.display = 'none';
- popupContainer.style.display = 'none';
- popupContainer.style.left = '50%';
- popupContainer.style.top = '50%';
- popupContainer.style.position = 'fixed';
- popupContainer.style.transform = 'translate(-50%, -50%) scale(1)';
- popupContainer.style.border = '';
- popupContainer.style.outline = '';
- backdrop.style.zIndex = '';
- backdrop.style.display = 'none';
- backdrop.style.backdropFilter = '';
- isLockedY = false;
- isLockedX = false;
- }
- popupContainer.addEventListener('mouseout', function(event) {
- let relatedTarget = event.relatedTarget;
- if (relatedTarget && (popupContainer.contains(relatedTarget) || relatedTarget.matches('.imgContainers') || relatedTarget.closest('.imgContainers'))) {
- return;
- }
- hidePopup();
- if (intervalP !== '') {
- popupTimer = setTimeout(() => {
- popupTimer = null;
- }, parseInt(intervalP));
- }
- });
- document.addEventListener('keydown', function(event) {
- if (event.key === "Escape") {
- event.preventDefault();
- hidePopup();
- }
- });
- //-------------------------------------------------------------------------
- // lock popup in screen
- let ishidePopupEnabled = true;
- function togglehidePopup(event) {
- ishidePopupEnabled = !ishidePopupEnabled;
- popupContainer.style.outline = ishidePopupEnabled ? '' : '3px solid #ae0001';
- }
- popupContainer.addEventListener('dblclick', function(event) {
- clearTimeout(clickTimeout);
- togglehidePopup();
- });
- backdrop.addEventListener('dblclick', function(event) {
- clearTimeout(clickTimeout);
- ishidePopupEnabled = true;
- hidePopup();
- });
- backdrop.addEventListener('click', function(event) {
- if (clickTimeout) clearTimeout(clickTimeout);
- clickTimeout = setTimeout(function() {
- if (isscaleTR) {
- isscaleTR = false;
- } else {
- backdrop.style.zIndex = '';
- backdrop.style.display = 'none';
- backdrop.style.backdropFilter = '';
- isshowPopupEnabled = false;
- }
- }, 300);
- });
- // Album scrolling
- LockedX.addEventListener('mouseenter', function() {
- LockedX.style.background = 'linear-gradient(to right, rgba(0, 0, 255, 0) 0%, rgba(0, 0, 255, 0.5) 25%, rgba(0, 0, 255, 0.5) 75%, rgba(0, 0, 255, 0) 100%)';
- });
- LockedX.addEventListener('mouseleave', function() {
- LockedX.style.background = '#0000';
- });
- LockedX.addEventListener('click', function(event) {
- ishidePopupEnabled = false;
- isLockedX = !isLockedX;
- if (isLockedX) {
- isLockedY = false;
- popupContainer.style.borderTop = '3px solid #00ff00';
- popupContainer.style.borderBottom = '3px solid #00ff00';
- popupContainer.style.borderLeft = '';
- popupContainer.style.borderRight = '';
- popupContainer.style.outline = '';
- } else {
- popupContainer.style.border = '';
- popupContainer.style.outline = '3px solid #ae0001';
- }
- });
- }
- //-------------------------------------------------------------------------
- // Is to be run -----------------------------------------------------------
- if (URLmatched) {
- 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() {
- enableP = 1 - enableP;
- indicatorBar.style.background = enableP ? 'linear-gradient(to right, rgba(50, 190, 152, 0) 0%, rgba(50, 190, 152, 0.5) 25%, rgba(50, 190, 152, 0.5) 75%, rgba(50, 190, 152, 0) 100%)' : 'linear-gradient(to right, rgba(174, 0, 1, 0) 0%, rgba(174, 0, 1, 0.5) 25%, rgba(174, 0, 1, 0.5) 75%, rgba(174, 0, 1, 0) 100%)';
- setTimeout(() => {
- indicatorBar.style.background = '#0000';
- }, 1000);
- if (enableP === 1) {
- HoverZoomMinus();
- } else {
- const existingPopup = document.body.querySelector('.popup-container');
- if (existingPopup) document.body.removeChild(existingPopup);
- const existingBackdrop = document.body.querySelector('.popup-backdrop');
- if (existingBackdrop) document.body.removeChild(existingBackdrop);
- }
- }
- let hoverTimeout;
- indicatorBar.addEventListener('mouseenter', () => {
- hoverTimeout = setTimeout(toggleIndicator, 500);
- });
- indicatorBar.addEventListener('mouseleave', () => {
- clearTimeout(hoverTimeout);
- indicatorBar.style.background = '#0000';
- });
- if (enableP === 1) {
- HoverZoomMinus();
- }
- } else {
- return;
- }
- })();