您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
可上下滚动翻页,并可调整"Fit"模式图像宽度。
- // ==UserScript==
- // @name:ko Hitomi 페이지 스크롤러
- // @name Hitomi page scroller
- // @name:ru Hitomi прокрутка страниц
- // @name:ja Hitomiページスクローラー
- // @name:zh-TW Hitomi頁面滾動條
- // @name:zh-CN Hitomi页面滚动条
- // @description:ko 위아래로 스크롤하여 페이지를 넘길 수 있으며, "Fit"모드 이미지 넓이를 조절 할 수 있습니다.
- // @description You can scroll up and down to turn the page and adjust the "Fit" mode image area.
- // @description:ru Вы можете прокручивать страницы вверх и вниз, регулируя ширину изображения в режиме "Fit".
- // @description:ja 上下にスクロールしてページをめくることができ、「Fit」モードイメージの広さを調節することができます。
- // @description:zh-TW 可上下滾動翻頁,並可調整"Fit"模式圖像寬度。
- // @description:zh-CN 可上下滚动翻页,并可调整"Fit"模式图像宽度。
- // @namespace https://ndaesik.tistory.com/
- // @version 2024.12.06.00.34
- // @author ndaesik
- // @icon https://t1.gstatic.com/faviconV2?client=SOCIAL&type=FAVICON&fallback_opts=TYPE,SIZE,URL&url=http://hitomi.la
- // @match https://*.la/reader/*
- // @grant GM_getValue
- // @grant GM_setValue
- // ==/UserScript==
- (function() {
- 'use strict';
- const startPage = parseInt(window.location.hash.slice(1)) || 1;
- let currentPage = startPage;
- let isLoading = false;
- let loadQueue = 0;
- let upScrollCount = 0;
- let lastScrollTime = 0;
- let initialLoad = true;
- const baseUrl = window.location.href.split('#')[0];
- let paddingSize = GM_getValue('paddingSize', 20);
- const style = document.createElement('style');
- style.textContent = `
- #comicImages {
- padding: 0 ${paddingSize}vw !important;
- width: 100vw !important;
- box-sizing: border-box !important;
- user-select: none !important;
- display: block !important;
- position: relative !important;
- }
- #comicImages picture {
- pointer-events: none !important;
- display: block !important;
- padding: 3px 0 !important
- }
- #comicImages img {
- width: 100% !important;
- display: block !important;
- }
- #comicImages.fitVertical img {
- max-height: unset !important;
- }
- .width-control-container {
- display: flex !important;
- align-items: center !important;
- gap: 10px !important;
- padding: 12px !important;
- }
- .width-range {
- width: 100px !important;
- }
- #comicImages picture:first-child {
- min-height: 100vh !important;
- }
- `;
- document.head.appendChild(style);
- function createPaddingControl() {
- const navbarNav = document.querySelector('.navbar-nav');
- if (!navbarNav) return;
- const container = document.createElement('div');
- container.className = 'width-control-container';
- const range = document.createElement('input');
- range.type = 'range';
- range.className = 'width-range';
- range.min = '0';
- range.max = '45';
- range.value = 45 - paddingSize;
- range.addEventListener('input', (e) => {
- paddingSize = 45 - e.target.value;
- document.querySelector('#comicImages').style.cssText += `padding: 0 ${paddingSize}vw !important;`;
- GM_setValue('paddingSize', paddingSize);
- });
- container.appendChild(range);
- navbarNav.appendChild(container);
- }
- function updateCurrentPage() {
- const container = document.querySelector('#comicImages');
- if (!container) return;
- const pictures = container.querySelectorAll('picture');
- if (!pictures.length) return;
- const pageSelect = document.querySelector('#single-page-select');
- if (!pageSelect) return;
- // 현재 뷰포트의 중앙 위치 계산
- const viewportHeight = window.innerHeight;
- const viewportCenter = window.scrollY + (viewportHeight / 2);
- // 각 picture 요소의 위치 확인
- let closestPicture = null;
- let closestDistance = Infinity;
- pictures.forEach((picture, index) => {
- const rect = picture.getBoundingClientRect();
- // picture 요소의 절대 위치 계산
- const pictureTop = rect.top + window.scrollY;
- const pictureCenter = pictureTop + (rect.height / 2);
- const distance = Math.abs(viewportCenter - pictureCenter);
- if (distance < closestDistance) {
- closestDistance = distance;
- closestPicture = index;
- }
- });
- // URL의 해시값을 기준으로 현재 페이지 계산
- if (closestPicture !== null) {
- const totalValue = parseInt(window.location.hash.slice(1)) + closestPicture;
- if (totalValue !== parseInt(pageSelect.value)) {
- pageSelect.value = totalValue;
- console.log(`Hash: ${window.location.hash}, Index: ${closestPicture}, Total: ${totalValue}`);
- }
- }
- }
- function handleScrollWheel(e) {
- const container = document.querySelector('#comicImages');
- if (!container) return;
- if (container.scrollTop === 0 && e.deltaY < 0) {
- const currentTime = Date.now();
- if (currentTime - lastScrollTime < 500) {
- upScrollCount++;
- if (upScrollCount >= 2) {
- const prevPanel = document.querySelector('#prevPanel');
- if (prevPanel) prevPanel.click();
- upScrollCount = 0;
- }
- } else {
- upScrollCount = 1;
- }
- lastScrollTime = currentTime;
- } else {
- upScrollCount = 0;
- }
- }
- function initScrollListener() {
- const container = document.querySelector('#comicImages');
- if (!container) {
- setTimeout(initScrollListener, 100);
- return;
- }
- let scrollTimeout;
- container.addEventListener('scroll', () => {
- if (scrollTimeout) return;
- scrollTimeout = setTimeout(() => {
- checkScrollAndLoad();
- updateCurrentPage();
- scrollTimeout = null;
- }, 50);
- });
- container.addEventListener('wheel', handleScrollWheel);
- container.style.cssText += `padding: 0 ${paddingSize}vw !important;`;
- document.querySelector('#single-page-select').value = startPage;
- if (initialLoad) {
- loadNextImage();
- initialLoad = false;
- }
- checkScrollAndLoad();
- updateCurrentPage();
- }
- function getMaxPage() {
- const options = document.querySelectorAll('#single-page-select option');
- let maxPage = 0;
- options.forEach(option => {
- const value = parseInt(option.value);
- if (value > maxPage) maxPage = value;
- });
- return maxPage;
- }
- async function loadNextImage() {
- if (isLoading) {
- loadQueue++;
- return;
- }
- const maxPage = getMaxPage();
- if (currentPage >= maxPage) {
- loadQueue = 0;
- return;
- }
- isLoading = true;
- try {
- currentPage++;
- const iframe = document.createElement('iframe');
- iframe.style.display = 'none';
- document.body.appendChild(iframe);
- iframe.src = `${baseUrl}#${currentPage}`;
- await new Promise(resolve => iframe.onload = resolve);
- const imgElement = await waitForElement(iframe, '#comicImages > picture > img');
- if (!imgElement?.src) throw new Error('Image not found');
- const pictureElement = document.createElement('picture');
- const newImage = document.createElement('img');
- newImage.src = imgElement.src;
- newImage.style.cssText = 'width: 100% !important; display: block !important;';
- await new Promise((resolve, reject) => {
- newImage.onload = resolve;
- newImage.onerror = reject;
- });
- pictureElement.appendChild(newImage);
- const container = document.querySelector('#comicImages');
- if (!container) throw new Error('Container not found');
- container.appendChild(pictureElement);
- iframe.remove();
- if (loadQueue > 0) {
- loadQueue--;
- loadNextImage();
- }
- checkScrollAndLoad();
- updateCurrentPage();
- } catch (error) {
- currentPage--;
- loadQueue = 0;
- } finally {
- isLoading = false;
- }
- }
- function checkScrollAndLoad() {
- const container = document.querySelector('#comicImages');
- if (!container) return;
- const scrollPosition = container.scrollTop + container.clientHeight;
- const remainingHeight = container.scrollHeight - scrollPosition;
- if (remainingHeight < container.clientHeight * 2.5) loadNextImage();
- }
- function waitForElement(iframe, selector, timeout = 5000) {
- return new Promise((resolve, reject) => {
- const startTime = Date.now();
- const check = () => {
- const element = iframe.contentDocument.querySelector(selector);
- if (element) return resolve(element);
- if (Date.now() - startTime > timeout) return reject(new Error(`Timeout`));
- setTimeout(check, 100);
- };
- check();
- });
- }
- createPaddingControl();
- initScrollListener();
- })();
QingJ © 2025
镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址