您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Naviguer entre les images d'un post sous forme de slideshow en cliquant sur une image sans ouvrir NoelShack.
当前为
// ==UserScript== // @name JVC_ImageViewer // @namespace http://tampermonkey.net/ // @version 1.35.1 // @description Naviguer entre les images d'un post sous forme de slideshow en cliquant sur une image sans ouvrir NoelShack. // @author HulkDu92 // @match https://*.jeuxvideo.com/forums/* // @grant none // @run-at document-end // @license MIT // ==/UserScript== (function() { 'use strict'; class ImageViewer { constructor(images, currentIndex) { this.images = images; this.currentIndex = currentIndex; this.overlay = null; this.imgElement = null; this.spinner = null; this.prevButton = null; this.nextButton = null; this.closeButton = null; this.infoText = null; this.zoomLevel = 1; // Niveau de zoom initial this.createOverlay(); this.updateImage(); } // Crée et configure les éléments du visualiseur d'images (overlay, boutons, texte d'information, etc.) createOverlay() { this.overlay = this.createElement('div', { position: 'fixed', top: 0, left: 0, width: '100%', height: '100%', backgroundColor: 'rgba(0, 0, 0, 0.8)', display: 'flex', alignItems: 'center', justifyContent: 'center', zIndex: 10000 }); this.imgElement = this.createElement('img', { maxWidth: '90%', maxHeight: '80%', objectFit: 'contain', transition: 'opacity 0.3s', opacity: 0, cursor: 'pointer' }); this.spinner = this.createSpinner(); this.prevButton = this.createButton('<', 'left'); this.nextButton = this.createButton('>', 'right'); this.closeButton = this.createCloseButton(); this.infoText = this.createInfoText(); // Événements associés aux boutons et à l'overlay this.addEventListeners(); // Ajout des éléments au DOM this.overlay.append(this.imgElement, this.spinner, this.prevButton, this.nextButton, this.closeButton, this.infoText); document.body.appendChild(this.overlay); } // Crée un élément HTML avec des styles createElement(tag, styles = {}) { const element = document.createElement(tag); Object.assign(element.style, styles); return element; } // Crée le bouton précédent ou suivant createButton(text, position) { const button = this.createElement('button', { position: 'absolute', [position]: '10px', backgroundColor: 'rgba(0, 0, 0, 0.6)', color: 'white', fontSize: '22px', border: 'none', borderRadius: '50%', width: '40px', height: '40px', cursor: 'pointer', display: 'flex', alignItems: 'center', justifyContent: 'center', boxShadow: '0px 4px 8px rgba(0, 0, 0, 0.6)', transition: 'background-color 0.3s, transform 0.3s' }); button.textContent = text; this.addButtonEffects(button); return button; } // Crée le bouton de fermeture createCloseButton() { const button = this.createElement('button', { position: 'absolute', top: '80px', right: '10px', backgroundColor: 'rgba(0, 0, 0, 0.8)', color: 'white', fontSize: '14px', border: 'none', borderRadius: '50%', width: '35px', height: '35px', cursor: 'pointer', zIndex: 10001 }); button.textContent = '✕'; this.addButtonEffects(button); return button; } // Crée la zone d'affichage du texte d'information (numéro d'image) createInfoText() { return this.createElement('div', { position: 'absolute', bottom: '10px', right: '10px', color: 'white', fontSize: '16px', backgroundColor: 'rgba(0, 0, 0, 0.5)', padding: '5px', borderRadius: '5px', zIndex: 10001 }); } // Crée un spinner pour indiquer le chargement de l'image createSpinner() { const spinner = this.createElement('div', { position: 'absolute', border: '8px solid #f3f3f3', borderTop: '8px solid #3498db', borderRadius: '50%', width: '50px', height: '50px', animation: 'spin 1s linear infinite', zIndex: 10001 }); return spinner; } addButtonEffects(button) { button.addEventListener('mouseenter', () => { button.style.backgroundColor = 'rgba(255, 255, 255, 0.8)'; button.style.color = 'black'; button.style.transform = 'scale(1.1)'; }); button.addEventListener('mouseleave', () => { button.style.backgroundColor = 'rgba(0, 0, 0, 0.6)'; button.style.color = 'white'; button.style.transform = 'scale(1)'; }); button.addEventListener('mousedown', () => { button.style.transform = 'scale(0.9)'; }); button.addEventListener('mouseup', () => { button.style.transform = 'scale(1.1)'; }); } // Ajoute les événements aux différents éléments du visualiseur addEventListeners() { this.prevButton.addEventListener('click', () => this.changeImage(-1)); this.nextButton.addEventListener('click', () => this.changeImage(1)); this.closeButton.addEventListener('click', () => this.closeViewer()); this.overlay.addEventListener('click', (event) => { if (event.target === this.overlay) { this.closeViewer(); } }); // Zoom avec la molette de la souris this.imgElement.addEventListener('wheel', (event) => this.handleZoom(event)); // Zoom pour appareils tactiles (iOS et Android) this.imgElement.addEventListener('gesturestart', (event) => { event.preventDefault(); this.handlePinchZoom(event); }); this.imgElement.addEventListener('click', () => { window.open(this.images[this.currentIndex].href, '_blank'); }); this.imgElement.addEventListener('click', () => { window.open(this.images[this.currentIndex].href, '_blank'); }); } // Gestion du zoom avec la molette de la souris handleZoom(event) { event.preventDefault(); const zoomIncrement = 0.1; if (event.deltaY < 0) { this.zoomLevel += zoomIncrement; // Zoomer } else { this.zoomLevel = Math.max(1, this.zoomLevel - zoomIncrement); // Dézoomer, mais ne pas descendre sous 1 } this.imgElement.style.transform = `scale(${this.zoomLevel})`; } // Gestion du zoom pour appareils tactiles (pincement) handlePinchZoom(event) { this.zoomLevel += event.scale - 1; // Ajuste le zoom en fonction de l'échelle du pincement this.imgElement.style.transform = `scale(${Math.max(1, this.zoomLevel)})`; // Ne pas descendre en dessous d'un zoom de 1 } // Met à jour l'image affichée dans le visualiseur updateImage() { const imageUrl = this.images[this.currentIndex].href; this.imgElement.src = imageUrl; this.infoText.textContent = `${this.currentIndex + 1} / ${this.images.length}`; this.spinner.style.display = 'block'; this.toggleButtonState(); this.imgElement.onload = () => { this.imgElement.style.opacity = 1; this.spinner.style.display = 'none'; }; this.imgElement.onerror = () => this.handleImageError(); } // Gestion des erreurs de chargement d'image handleImageError() { const miniUrl = this.images[this.currentIndex].querySelector('img').src; const updatedUrl = miniUrl.replace('/minis/', '/fichiers/').replace('.png', '.jpg'); this.imgElement.src = updatedUrl; this.imgElement.onerror = () => { const secondTryUrl = miniUrl.replace('/minis/', '/fichiers/').replace('.jpg', '.png'); this.imgElement.src = secondTryUrl; this.imgElement.onerror = () => { this.imgElement.src = miniUrl; }; }; } // Change d'image en fonction de la direction (suivant/précédent) changeImage(direction) { this.currentIndex = (this.currentIndex + direction + this.images.length) % this.images.length; this.imgElement.style.opacity = 0; this.spinner.style.display = 'block'; this.updateImage(); } // Ferme le visualiseur d'images closeViewer() { document.body.removeChild(this.overlay); } // Désactive ou active les boutons suivant/précédent en fonction de l'index actuel toggleButtonState() { if (this.currentIndex === 0) { this.prevButton.disabled = true; this.prevButton.style.opacity = 0.5; this.prevButton.style.cursor = 'initial'; } else { this.prevButton.disabled = false; this.prevButton.style.opacity = 1; this.prevButton.style.cursor = 'pointer'; } if (this.currentIndex === this.images.length - 1) { this.nextButton.disabled = true; this.nextButton.style.opacity = 0.5; this.nextButton.style.cursor = 'initial'; } else { this.nextButton.disabled = false; this.nextButton.style.opacity = 1; this.nextButton.style.cursor = 'pointer'; } } } // Ajout des animations pour le spinner const style = document.createElement('style'); style.textContent = ` @keyframes spin { 0% { transform: rotate(0deg); } 100% { transform: rotate(360deg); } } `; document.head.appendChild(style); // Ajoute des écouteurs d'événements aux images sur la page function addListeners() { const selectors = ['.txt-msg a', '.message a']; selectors.forEach(selector => { document.querySelectorAll(selector).forEach(link => { link.addEventListener('click', function(event) { const imgElement = this.querySelector('img'); if (imgElement) { event.preventDefault(); const images = Array.from(this.closest('.txt-msg, .message').querySelectorAll('a')).filter(imgLink => imgLink.querySelector('img')); const currentIndex = images.indexOf(this); new ImageViewer(images, currentIndex); } }, true); }); }); } // Surveille les changements dans le DOM pour ajouter des événements aux nouveaux éléments const observer = new MutationObserver(() => addListeners()); const targetNode = document.querySelector('#page-messages-forum'); const config = { childList: true, subtree: true }; if (targetNode) { observer.observe(targetNode, config); } addListeners(); })();