mirror of
https://github.com/moku-project/Moku.git
synced 2026-06-13 01:09:56 -05:00
[V1] Addressed Laggy Single-Page (Applied Cache-Loading)
This commit is contained in:
@@ -329,8 +329,11 @@ export default function Reader() {
|
|||||||
// Discard result if the user has already navigated to a different chapter
|
// Discard result if the user has already navigated to a different chapter
|
||||||
if (loadingChapterRef.current !== targetId) return;
|
if (loadingChapterRef.current !== targetId) return;
|
||||||
|
|
||||||
// Decode the first page before committing so no previous chapter flashes
|
// Decode the first page before committing so no previous chapter flashes.
|
||||||
await decodeImage(urls[0]);
|
// In longstrip mode skip the blocking decode — images stream in naturally.
|
||||||
|
if (style !== "longstrip") {
|
||||||
|
await decodeImage(urls[0]);
|
||||||
|
}
|
||||||
|
|
||||||
if (loadingChapterRef.current !== targetId) return;
|
if (loadingChapterRef.current !== targetId) return;
|
||||||
|
|
||||||
@@ -348,10 +351,14 @@ export default function Reader() {
|
|||||||
setStripChapters([]);
|
setStripChapters([]);
|
||||||
setVisibleChapterId(null);
|
setVisibleChapterId(null);
|
||||||
}
|
}
|
||||||
|
// Only clear loading after state is fully committed — no flash frames
|
||||||
|
setLoading(false);
|
||||||
})
|
})
|
||||||
.catch((e) => setError(e instanceof Error ? e.message : String(e)))
|
.catch((e) => {
|
||||||
.finally(() => {
|
if (loadingChapterRef.current === targetId) {
|
||||||
if (loadingChapterRef.current === targetId) setLoading(false);
|
setError(e instanceof Error ? e.message : String(e));
|
||||||
|
setLoading(false);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}, [activeChapter?.id]);
|
}, [activeChapter?.id]);
|
||||||
|
|
||||||
@@ -507,6 +514,7 @@ export default function Reader() {
|
|||||||
}, [pageGroups, pageNumber, adjacent, activeChapterList]);
|
}, [pageGroups, pageNumber, adjacent, activeChapterList]);
|
||||||
|
|
||||||
const goForward = useCallback(() => {
|
const goForward = useCallback(() => {
|
||||||
|
if (loading || !pageUrls.length) return;
|
||||||
if (style === "double" && pageGroups.length) { advanceGroup(true); return; }
|
if (style === "double" && pageGroups.length) { advanceGroup(true); return; }
|
||||||
if (pageNumber < lastPage) {
|
if (pageNumber < lastPage) {
|
||||||
const nextUrl = pageUrls[pageNumber]; // pageNumber is 1-based, so index is pageNumber
|
const nextUrl = pageUrls[pageNumber]; // pageNumber is 1-based, so index is pageNumber
|
||||||
@@ -521,9 +529,10 @@ export default function Reader() {
|
|||||||
} else {
|
} else {
|
||||||
closeReader();
|
closeReader();
|
||||||
}
|
}
|
||||||
}, [pageNumber, lastPage, pageUrls, adjacent, activeChapterList, style, pageGroups, advanceGroup]);
|
}, [loading, pageNumber, lastPage, pageUrls, adjacent, activeChapterList, style, pageGroups, advanceGroup]);
|
||||||
|
|
||||||
const goBack = useCallback(() => {
|
const goBack = useCallback(() => {
|
||||||
|
if (loading || !pageUrls.length) return;
|
||||||
if (style === "double" && pageGroups.length) { advanceGroup(false); return; }
|
if (style === "double" && pageGroups.length) { advanceGroup(false); return; }
|
||||||
if (pageNumber > 1) {
|
if (pageNumber > 1) {
|
||||||
const prevUrl = pageUrls[pageNumber - 2]; // 0-based index of previous page
|
const prevUrl = pageUrls[pageNumber - 2]; // 0-based index of previous page
|
||||||
@@ -535,7 +544,7 @@ export default function Reader() {
|
|||||||
} else if (adjacent.prev) {
|
} else if (adjacent.prev) {
|
||||||
openReader(adjacent.prev, activeChapterList);
|
openReader(adjacent.prev, activeChapterList);
|
||||||
}
|
}
|
||||||
}, [pageNumber, pageUrls, adjacent, activeChapterList, style, pageGroups, advanceGroup]);
|
}, [loading, pageNumber, pageUrls, adjacent, activeChapterList, style, pageGroups, advanceGroup]);
|
||||||
|
|
||||||
const goNext = rtl ? goBack : goForward;
|
const goNext = rtl ? goBack : goForward;
|
||||||
const goPrev = rtl ? goForward : goBack;
|
const goPrev = rtl ? goForward : goBack;
|
||||||
@@ -600,8 +609,8 @@ export default function Reader() {
|
|||||||
else if (matchesKeybind(e, kb.pageLeft)) { e.preventDefault(); goBack(); }
|
else if (matchesKeybind(e, kb.pageLeft)) { e.preventDefault(); goBack(); }
|
||||||
else if (matchesKeybind(e, kb.firstPage)) { e.preventDefault(); setPageNumber(1); }
|
else if (matchesKeybind(e, kb.firstPage)) { e.preventDefault(); setPageNumber(1); }
|
||||||
else if (matchesKeybind(e, kb.lastPage)) { e.preventDefault(); setPageNumber(lastPage); }
|
else if (matchesKeybind(e, kb.lastPage)) { e.preventDefault(); setPageNumber(lastPage); }
|
||||||
else if (matchesKeybind(e, kb.chapterRight)) { e.preventDefault(); if (adjacent.next) openReader(adjacent.next, activeChapterList); }
|
else if (matchesKeybind(e, kb.chapterRight)) { e.preventDefault(); if (!loading && adjacent.next) openReader(adjacent.next, activeChapterList); }
|
||||||
else if (matchesKeybind(e, kb.chapterLeft)) { e.preventDefault(); if (adjacent.prev) openReader(adjacent.prev, activeChapterList); }
|
else if (matchesKeybind(e, kb.chapterLeft)) { e.preventDefault(); if (!loading && adjacent.prev) openReader(adjacent.prev, activeChapterList); }
|
||||||
else if (matchesKeybind(e, kb.togglePageStyle)) { e.preventDefault(); cycleStyle(); }
|
else if (matchesKeybind(e, kb.togglePageStyle)) { e.preventDefault(); cycleStyle(); }
|
||||||
else if (matchesKeybind(e, kb.toggleReadingDirection)) { e.preventDefault(); updateSettings({ readingDirection: rtl ? "ltr" : "rtl" }); }
|
else if (matchesKeybind(e, kb.toggleReadingDirection)) { e.preventDefault(); updateSettings({ readingDirection: rtl ? "ltr" : "rtl" }); }
|
||||||
else if (matchesKeybind(e, kb.toggleFullscreen)) { e.preventDefault(); toggleFullscreen().catch(console.error); }
|
else if (matchesKeybind(e, kb.toggleFullscreen)) { e.preventDefault(); toggleFullscreen().catch(console.error); }
|
||||||
@@ -609,7 +618,7 @@ export default function Reader() {
|
|||||||
};
|
};
|
||||||
window.addEventListener("keydown", onKey);
|
window.addEventListener("keydown", onKey);
|
||||||
return () => window.removeEventListener("keydown", onKey);
|
return () => window.removeEventListener("keydown", onKey);
|
||||||
}, [goForward, goBack, kb, style, rtl, lastPage, adjacent, activeChapterList, zoomOpen, dlOpen, maxW]);
|
}, [goForward, goBack, kb, style, rtl, lastPage, adjacent, activeChapterList, zoomOpen, dlOpen, maxW, loading]);
|
||||||
|
|
||||||
// ── Longstrip scroll tracker ─────────────────────────────────────────────────
|
// ── Longstrip scroll tracker ─────────────────────────────────────────────────
|
||||||
// Tracks current page number. In autoNext mode, appends the next chapter's
|
// Tracks current page number. In autoNext mode, appends the next chapter's
|
||||||
@@ -935,11 +944,11 @@ export default function Reader() {
|
|||||||
) : (
|
) : (
|
||||||
pageReady && (
|
pageReady && (
|
||||||
<img
|
<img
|
||||||
key={pageNumber}
|
|
||||||
src={pageUrls[pageNumber - 1]}
|
src={pageUrls[pageNumber - 1]}
|
||||||
alt={`Page ${pageNumber}`}
|
alt={`Page ${pageNumber}`}
|
||||||
className={imgCls}
|
className={imgCls}
|
||||||
decoding="async"
|
decoding="async"
|
||||||
|
style={{ transition: "opacity 0.1s ease" }}
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
)}
|
)}
|
||||||
@@ -947,10 +956,10 @@ export default function Reader() {
|
|||||||
|
|
||||||
{/* ── Bottom nav ── */}
|
{/* ── Bottom nav ── */}
|
||||||
<div className={[s.bottombar, uiVisible ? "" : s.uiHidden].join(" ")}>
|
<div className={[s.bottombar, uiVisible ? "" : s.uiHidden].join(" ")}>
|
||||||
<button className={s.navBtn} onClick={goPrev} disabled={pageNumber === 1 && !adjacent.prev}>
|
<button className={s.navBtn} onClick={goPrev} disabled={loading || (pageNumber === 1 && !adjacent.prev)}>
|
||||||
<ArrowLeft size={13} weight="light" />
|
<ArrowLeft size={13} weight="light" />
|
||||||
</button>
|
</button>
|
||||||
<button className={s.navBtn} onClick={goNext} disabled={pageNumber === lastPage && !adjacent.next}>
|
<button className={s.navBtn} onClick={goNext} disabled={loading || (pageNumber === lastPage && !adjacent.next)}>
|
||||||
<ArrowRight size={13} weight="light" />
|
<ArrowRight size={13} weight="light" />
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
+1
-1
@@ -64,7 +64,7 @@ export interface Settings {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const DEFAULT_SETTINGS: Settings = {
|
export const DEFAULT_SETTINGS: Settings = {
|
||||||
pageStyle: "single",
|
pageStyle: "longstrip",
|
||||||
readingDirection: "ltr",
|
readingDirection: "ltr",
|
||||||
fitMode: "width",
|
fitMode: "width",
|
||||||
maxPageWidth: 900,
|
maxPageWidth: 900,
|
||||||
|
|||||||
Reference in New Issue
Block a user