您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Interactive Inline Gallery Carousel with full-res Images for Reddit Search Gallery.
当前为
// ==UserScript== // @name Reddit Inline Image Gallery Carousel // @namespace http://tampermonkey.net/ // @version 1.0 // @description Interactive Inline Gallery Carousel with full-res Images for Reddit Search Gallery. // @author UniverseDev // @license GPL-3.0-or-later // @icon https://www.reddit.com/favicon.ico // @match https://www.reddit.com/search/*type=media // @match https://www.reddit.com/search/?q=.*&type=media.* // @grant GM_addStyle // @grant GM.xmlHttpRequest // ==/UserScript== (() => { 'use strict'; GM_addStyle(` .reddit-carousel { position: relative; overflow: hidden; width: 100%; min-height: 200px; margin-bottom: 10px; border: 1px solid #ddd; cursor: default; } .reddit-carousel-slide-container { display: flex; transition: transform 125ms ease; } .reddit-carousel-slide { flex: 0 0 100%; width: 100%; text-align: center; min-height: 200px; } .reddit-carousel-slide img { max-width: 100%; width: 100%; height: auto; object-fit: contain; display: block; margin: 0 auto; } .reddit-carousel-error { color: red; font-size: 14px; padding: 20px; } .reddit-carousel-arrow { position: absolute; top: 50%; transform: translateY(-50%); background: rgba(0,0,0,0.7); border: none; width: 30px; height: 30px; cursor: pointer; border-radius: 50%; display: flex; align-items: center; justify-content: center; z-index: 2; transition: background 0.2s ease, transform 0.15s ease; color: #fff; box-shadow: 0 2px 4px rgba(0,0,0,0.4); } .reddit-carousel-arrow:hover { background: rgba(0,0,0,0.85); transform: translateY(-50%) scale(1.1); box-shadow: 0 4px 8px rgba(0,0,0,0.5); } .reddit-carousel-arrow:active { transform: translateY(-50%) scale(0.95); } .reddit-carousel-arrow svg { width: 16px; height: 16px; fill: currentColor; } .reddit-carousel-arrow.left { left: 10px; display: none; } .reddit-carousel-arrow.right { right: 10px; display: flex; } `); const redditCarousel_animateTransition = (container, start, end, duration, callback) => { const startTime = performance.now(); const step = now => { const progress = Math.min((now - startTime) / duration, 1); const ease = 1 - Math.pow(1 - progress, 3); container.style.transform = `translateX(${start + (end - start) * ease}px)`; if (progress < 1) requestAnimationFrame(step); else if (callback) callback(); }; requestAnimationFrame(step); }; const redditCarousel_createCarousel = urls => { if (!urls?.length) return null; const carousel = document.createElement('div'); carousel.classList.add('reddit-carousel'); carousel.addEventListener('click', e => { e.stopPropagation(); e.preventDefault(); }); const slideContainer = document.createElement('div'); slideContainer.classList.add('reddit-carousel-slide-container'); carousel.appendChild(slideContainer); urls.forEach(url => { const slide = document.createElement('div'); slide.classList.add('reddit-carousel-slide'); const img = document.createElement('img'); img.src = url; img.alt = "Reddit Gallery Image"; img.onerror = function() { slide.innerHTML = '<div class="reddit-carousel-error">Image failed to load</div>'; }; img.addEventListener('load', () => { redditCarousel_recalcDimensions(); redditCarousel_updateArrowVisibility(); updateArrowPositions(); }); slide.appendChild(img); slideContainer.appendChild(slide); }); const extraCounterWrapper = document.createElement('div'); extraCounterWrapper.innerHTML = `<div class="absolute inset-0 overflow-visible flex items-right justify-end"> <button rpl="" class="pointer-events-none m-xs leading-4 pl-2xs pr-2xs py-0 text-sm h-fit button-small px-[var(--rem10)] button-media items-center justify-center button inline-flex "> <span class="flex items-center justify-center"> <span class="extra-counter-text flex items-center gap-xs">1/${urls.length}</span> </span> </button> </div>`; carousel.appendChild(extraCounterWrapper); let redditCarousel_currentIndex = 0, redditCarousel_currentOffset = 0; const redditCarousel_recalcDimensions = () => { const containerWidth = slideContainer.clientWidth; redditCarousel_currentOffset = -redditCarousel_currentIndex * containerWidth; slideContainer.style.transform = `translateX(${redditCarousel_currentOffset}px)`; }; const redditCarousel_updateCounter = () => { const newCounterText = `${redditCarousel_currentIndex + 1}/${urls.length}`; const extraCounterText = carousel.querySelector('.extra-counter-text'); if (extraCounterText) { extraCounterText.textContent = newCounterText; } }; const redditCarousel_goToSlide = index => { index = Math.max(0, Math.min(index, urls.length - 1)); const containerWidth = slideContainer.clientWidth; const startOffset = redditCarousel_currentOffset; const endOffset = -index * containerWidth; redditCarousel_animateTransition(slideContainer, startOffset, endOffset, 125, () => { redditCarousel_currentOffset = endOffset; redditCarousel_currentIndex = index; redditCarousel_updateCounter(); redditCarousel_updateArrowVisibility(); updateArrowPositions(); }); }; let redditCarousel_leftArrow, redditCarousel_rightArrow; if (urls.length > 1) { const redditCarousel_createArrow = dir => { const btn = document.createElement('button'); btn.classList.add('reddit-carousel-arrow', dir); btn.addEventListener('click', e => { e.stopPropagation(); e.preventDefault(); }); const svg = document.createElementNS("http://www.w3.org/2000/svg", "svg"); svg.setAttribute("viewBox", "0 0 20 20"); const path = document.createElementNS("http://www.w3.org/2000/svg", "path"); path.setAttribute("d", dir === 'left' ? "M12.793 19.707l-9-9a1 1 0 0 1 0-1.414l9-9 1.414 1.414L5.914 10l8.293 8.293-1.414 1.414Z" : "M7.207 19.707l-1.414-1.414L14.086 10 5.793 1.707 7.207.293l9 9a1 1 0 0 1 0 1.414l-9 9Z" ); svg.appendChild(path); btn.appendChild(svg); return btn; }; redditCarousel_leftArrow = redditCarousel_createArrow('left'); redditCarousel_leftArrow.addEventListener('click', () => { redditCarousel_goToSlide(redditCarousel_currentIndex - 1); }); carousel.appendChild(redditCarousel_leftArrow); redditCarousel_rightArrow = redditCarousel_createArrow('right'); redditCarousel_rightArrow.addEventListener('click', () => { redditCarousel_goToSlide(redditCarousel_currentIndex + 1); }); carousel.appendChild(redditCarousel_rightArrow); } const redditCarousel_updateArrowVisibility = () => { if (urls.length <= 1) return; if (redditCarousel_currentIndex === 0) { redditCarousel_leftArrow.style.display = 'none'; redditCarousel_rightArrow.style.display = 'flex'; } else if (redditCarousel_currentIndex === urls.length - 1) { redditCarousel_leftArrow.style.display = 'flex'; redditCarousel_rightArrow.style.display = 'none'; } else { redditCarousel_leftArrow.style.display = 'flex'; redditCarousel_rightArrow.style.display = 'flex'; } }; const updateArrowPositions = () => { requestAnimationFrame(() => { const rect = carousel.getBoundingClientRect(); const arrowTop = (rect.height - 30) / 2; if (redditCarousel_leftArrow) { redditCarousel_leftArrow.style.top = arrowTop + 'px'; } if (redditCarousel_rightArrow) { redditCarousel_rightArrow.style.top = arrowTop + 'px'; } }); }; updateArrowPositions(); let redditCarousel_resizeTimeout; window.addEventListener('resize', () => { clearTimeout(redditCarousel_resizeTimeout); redditCarousel_resizeTimeout = setTimeout(() => { redditCarousel_recalcDimensions(); redditCarousel_updateArrowVisibility(); updateArrowPositions(); }, 100); }); redditCarousel_updateArrowVisibility(); return carousel; }; const redditCarousel_fetchAndProcessGallery = (postURL, container) => { GM.xmlHttpRequest({ url: `${postURL}.json`, method: 'GET', onload: response => { if (response.status >= 200 && response.status < 300) { try { const jsonData = JSON.parse(response.responseText); const postData = jsonData[0]?.data?.children[0]?.data; if (!postData?.gallery_data) return; const { items } = postData.gallery_data; const mediaMeta = postData.media_metadata; const urls = items.reduce((acc, item) => { const meta = mediaMeta[item.media_id]; if (meta && meta.id && meta.m) { let ext = "jpg"; if (meta.m.includes("png")) ext = "png"; else if (meta.m.includes("gif")) ext = "gif"; else if (meta.m.includes("webp")) ext = "webp"; acc.push(`https://i.redd.it/${meta.id}.${ext}`); } return acc; }, []); const carousel = redditCarousel_createCarousel(urls); if (carousel) { container.innerHTML = ''; container.appendChild(carousel); const parentLink = container.closest('a'); if (parentLink) { parentLink.addEventListener('click', e => { e.stopPropagation(); e.preventDefault(); }); } } } catch (error) { console.error("JSON parse error:", error); } } }, onerror: err => console.error("Request failed:", err) }); }; const redditCarousel_galleryObserver = new IntersectionObserver((entries, observer) => { entries.forEach(entry => { if (entry.isIntersecting) { const container = entry.target; if (!container.hasAttribute('data-gallery-intersected')) { container.setAttribute('data-gallery-intersected', 'true'); const postUnit = container.closest('div[data-id="search-media-post-unit"]'); const postLink = postUnit?.querySelector('a.no-underline'); if (postLink?.href) { redditCarousel_fetchAndProcessGallery(postLink.href, container); } } observer.unobserve(container); } }); }, { threshold: 0.1 }); const redditCarousel_processSearchResults = () => { document.querySelectorAll('div[data-id="search-media-post-unit"]').forEach(post => { if (post.hasAttribute('data-gallery-checked')) return; post.setAttribute('data-gallery-checked', 'true'); const indicator = post.querySelector('div.absolute.inset-0.overflow-visible.flex.items-right.justify-end button span'); if (indicator?.textContent.includes('/')) { const container = post.querySelector('shreddit-aspect-ratio'); if (container) redditCarousel_galleryObserver.observe(container); } }); }; let redditCarousel_ticking = false; window.addEventListener('scroll', () => { if (!redditCarousel_ticking) { requestAnimationFrame(() => { redditCarousel_processSearchResults(); redditCarousel_ticking = false; }); redditCarousel_ticking = true; } }); window.addEventListener('load', redditCarousel_processSearchResults); })();