feat: preload the current chapter ahead of the reader
Continuous mode eager-loaded only pages 0..=initialIndex; everything below stayed `loading="lazy"`, so pages fetched and reflowed in as they scrolled into view. Now a rolling window eager-loads a few pages ahead of the reading position (PRELOAD_AHEAD) so they arrive — fetched and sized — while still below the fold, then swap in without pop-in. The window rolls forward off `scrollLeadIdx`, the top-of-viewport page measured from live rects and guarded by `scrollY`. It deliberately does not use the read-progress high-water mark: before images load they are 0-height and pile into the viewport, which would latch that mark to the last page and eager-load the whole chapter on open. Pages past the window stay lazy so opening a long chapter doesn't fetch every page at once. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -171,41 +171,39 @@ test.describe('reader ?page=N deep link', () => {
|
||||
await expect(page.getByTestId('reader-continuous')).toBeVisible();
|
||||
|
||||
// Pages 1..=4 (1-indexed in the testid, 0-indexed in the
|
||||
// template; initialIndex = 3 means we eager-load 0..=3, i.e.
|
||||
// testids 1..4). Without this guard, page 2+ would be lazy
|
||||
// template; initialIndex = 3 means we eager-load at least 0..=3,
|
||||
// i.e. testids 1..4). Without this guard, page 2+ would be lazy
|
||||
// and their 0×0 placeholders would let the scroll target
|
||||
// appear far above its final position.
|
||||
for (const n of [1, 2, 3, 4]) {
|
||||
// appear far above its final position. The rolling preload window
|
||||
// (PRELOAD_AHEAD) extends the eager set a few pages past the
|
||||
// target, so on this 6-page chapter every page is eager.
|
||||
for (const n of [1, 2, 3, 4, 5, 6]) {
|
||||
await expect(page.getByTestId(`reader-page-${n}`)).toHaveAttribute(
|
||||
'loading',
|
||||
'eager'
|
||||
);
|
||||
}
|
||||
// Pages beyond the target stay lazy.
|
||||
for (const n of [5, 6]) {
|
||||
await expect(page.getByTestId(`reader-page-${n}`)).toHaveAttribute(
|
||||
'loading',
|
||||
'lazy'
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
test('continuous mode: no ?page= eager-loads only the first two', async ({
|
||||
test('continuous mode: no ?page= eager-loads a leading preload window, not the whole chapter', async ({
|
||||
page
|
||||
}) => {
|
||||
await mockReader(page, 'continuous');
|
||||
await page.goto(`/manga/${mangaId}/chapter/${chapterId}`);
|
||||
|
||||
await expect(page.getByTestId('reader-continuous')).toBeVisible();
|
||||
await expect(page.getByTestId('reader-page-1')).toHaveAttribute(
|
||||
'loading',
|
||||
'eager'
|
||||
);
|
||||
await expect(page.getByTestId('reader-page-2')).toHaveAttribute(
|
||||
'loading',
|
||||
'eager'
|
||||
);
|
||||
await expect(page.getByTestId('reader-page-3')).toHaveAttribute(
|
||||
// initialIndex = 0; the rolling window (PRELOAD_AHEAD = 3) eager-loads
|
||||
// the first few pages ahead of the reader so they don't pop in on
|
||||
// scroll ...
|
||||
for (const n of [1, 2, 3]) {
|
||||
await expect(page.getByTestId(`reader-page-${n}`)).toHaveAttribute(
|
||||
'loading',
|
||||
'eager'
|
||||
);
|
||||
}
|
||||
// ... while the tail stays lazy so a long chapter doesn't fetch every
|
||||
// page at once on open.
|
||||
await expect(page.getByTestId('reader-page-6')).toHaveAttribute(
|
||||
'loading',
|
||||
'lazy'
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user