IntersectionObserver 후킹으로 백그라운드 탭 이미지 로딩 문제 해결
- @run-at document-start로 변경하여 페이지 스크립트보다 먼저 실행 - IntersectionObserver.observe() 후킹: observe() 즉시 콜백 실행하도록 하여 백그라운드 탭에서도 IntersectionObserver가 동작하게 함 - 기존 로직은 DOMContentLoaded 후 실행되도록 main() 함수로 분리 - scrollHeight 동적 대응 로직 유지
This commit is contained in:
@@ -13,13 +13,48 @@
|
||||
// @grant GM_download
|
||||
// @require https://cdnjs.cloudflare.com/ajax/libs/jszip/3.7.1/jszip.min.js
|
||||
// @require https://cdnjs.cloudflare.com/ajax/libs/jszip-utils/0.1.0/jszip-utils.js
|
||||
// @run-at document-end
|
||||
// @run-at document-start
|
||||
// @license MIT
|
||||
// ==/UserScript==
|
||||
|
||||
(function () {
|
||||
'use strict';
|
||||
|
||||
// ---------------------------------------------------------------
|
||||
// Phase 1: IntersectionObserver 후킹 (document-start 에서 즉시 실행)
|
||||
// 백그라운드 탭에서 IO가 동작하지 않아도 observe() 즉시 콜백 실행
|
||||
// ---------------------------------------------------------------
|
||||
const OrigIO = window.IntersectionObserver;
|
||||
if (OrigIO) {
|
||||
const HookedIO = function (callback, options) {
|
||||
const instance = new OrigIO(callback, options);
|
||||
const origObserve = instance.observe.bind(instance);
|
||||
instance.observe = function (target) {
|
||||
origObserve(target);
|
||||
setTimeout(() => {
|
||||
try {
|
||||
callback([{
|
||||
target: target,
|
||||
isIntersecting: true,
|
||||
intersectionRatio: 1,
|
||||
boundingClientRect: target.getBoundingClientRect(),
|
||||
intersectionRect: target.getBoundingClientRect(),
|
||||
rootBounds: null,
|
||||
time: performance.now()
|
||||
}], instance);
|
||||
} catch (_) { }
|
||||
}, 50);
|
||||
};
|
||||
return instance;
|
||||
};
|
||||
HookedIO.prototype = OrigIO.prototype;
|
||||
window.IntersectionObserver = HookedIO;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------
|
||||
// Phase 2: DOM 로딩 후 나머지 로직
|
||||
// ---------------------------------------------------------------
|
||||
const main = () => {
|
||||
// 사이트가 console을 모니터링/차단하므로 자체 로거 사용
|
||||
const log = (...args) => {
|
||||
try { console.log(...args); } catch (_) {}
|
||||
@@ -98,7 +133,7 @@
|
||||
}
|
||||
|
||||
const comicInfo = `<?xml version="1.0" encoding="utf-8"?>
|
||||
<ComicInfo xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
|
||||
<ComicInfo xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
|
||||
<Title>${title}</Title>
|
||||
<Series>${series}</Series>
|
||||
<Number>${number}</Number>
|
||||
@@ -124,8 +159,8 @@
|
||||
<Format></Format>
|
||||
<BlackAndWhite>Unknown</BlackAndWhite>
|
||||
<Manga>Yes</Manga>
|
||||
${pagesXml ? ` <Pages>\n${pagesXml} </Pages>\n` : ''}
|
||||
</ComicInfo>`;
|
||||
${pagesXml ? ` <Pages>\n${pagesXml} </Pages>\n` : ''}
|
||||
</ComicInfo>`;
|
||||
return comicInfo;
|
||||
}
|
||||
|
||||
@@ -187,17 +222,23 @@ ${pagesXml ? ` <Pages>\n${pagesXml} </Pages>\n` : ''}
|
||||
return { isListPage, isEpisodePage };
|
||||
}
|
||||
|
||||
// lazy loading 트리거: data-src 복사 + loading=eager + 스크롤
|
||||
const triggerLazyLoading = async () => {
|
||||
const viewer = document.querySelector('.vw-imgs, [class*="vw-imgs"]') || document;
|
||||
const imgs = viewer.querySelectorAll('img');
|
||||
imgs.forEach(img => {
|
||||
// 이미지의 data-* 속성을 src로 강제 복사
|
||||
const forceLoadImages = (root) => {
|
||||
const container = root || document;
|
||||
container.querySelectorAll('img').forEach(img => {
|
||||
img.setAttribute('loading', 'eager');
|
||||
['data-src', 'data-original', 'data-lazy-src', 'data-lazy'].forEach(attr => {
|
||||
['data-src', 'data-original', 'data-lazy-src', 'data-lazy', 'data-srcset'].forEach(attr => {
|
||||
const val = img.getAttribute(attr);
|
||||
if (val && !img.src) img.src = val;
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
// lazy loading 트리거: data-src 복사 + loading=eager + 스크롤
|
||||
const triggerLazyLoading = async () => {
|
||||
const viewer = document.querySelector('.vw-imgs, [class*="vw-imgs"]') || document;
|
||||
// 우선 현재 img들 강제 로딩
|
||||
forceLoadImages(viewer);
|
||||
// 동적 스크롤: 새 콘텐츠가 계속 로드되면 scrollHeight도 계속 증가
|
||||
let prevHeight = 0;
|
||||
let stuckCount = 0;
|
||||
@@ -219,9 +260,12 @@ ${pagesXml ? ` <Pages>\n${pagesXml} </Pages>\n` : ''}
|
||||
await sleep(300);
|
||||
}
|
||||
window.scrollTo(0, maxScroll);
|
||||
// 스크롤 후 추가된 img들도 강제 로딩 (백그라운드 탭 IO 대체)
|
||||
forceLoadImages(viewer);
|
||||
await sleep(1500);
|
||||
}
|
||||
// 끝까지 다 내려간 상태에서 여유 대기
|
||||
// 끝까지 다 내려간 상태에서 여유 대기 + 최종 강제 로딩
|
||||
forceLoadImages(viewer);
|
||||
await sleep(2000);
|
||||
window.scrollTo(0, 0);
|
||||
await sleep(1000);
|
||||
@@ -403,4 +447,12 @@ ${pagesXml ? ` <Pages>\n${pagesXml} </Pages>\n` : ''}
|
||||
window.addEventListener('popstate', () => setTimeout(registerMenu, 500));
|
||||
|
||||
log('sbxh6Downloader loaded');
|
||||
};
|
||||
|
||||
// DOM 로딩 완료 후 main() 실행
|
||||
if (document.readyState === 'loading') {
|
||||
document.addEventListener('DOMContentLoaded', main);
|
||||
} else {
|
||||
main();
|
||||
}
|
||||
})();
|
||||
|
||||
Reference in New Issue
Block a user