fix(pdf): prevent continuous scroll kickback (#3990)

Update foliate-js to preserve the current intra-page scroll anchor when fixed-layout pages relayout or replace placeholders in scrolled mode, instead of snapping back with scrollIntoView().

Add regression coverage for the scroll-anchor restore path.

Closes #3876.
This commit is contained in:
Huang Xin
2026-04-29 02:26:55 +08:00
committed by GitHub
parent ad55375f89
commit dab92c8a46
2 changed files with 56 additions and 1 deletions
@@ -0,0 +1,55 @@
import { describe, expect, it } from 'vitest';
import { captureScrollModeAnchor, restoreScrollModeAnchor } from 'foliate-js/fixed-layout.js';
describe('fixed-layout scroll mode anchor preservation', () => {
it('captures the current intra-page offset', () => {
const anchor = captureScrollModeAnchor(
[
{ index: 0, top: 0, height: 1000 },
{ index: 1, top: 1008, height: 1000 },
],
1350,
1,
);
expect(anchor).toEqual({
index: 1,
fraction: 0.342,
scrollTop: 1350,
});
});
it('restores the same intra-page position after page sizes change', () => {
const anchor = captureScrollModeAnchor(
[
{ index: 0, top: 0, height: 1000 },
{ index: 1, top: 1008, height: 1000 },
],
1350,
1,
);
const restored = restoreScrollModeAnchor(
[
{ index: 0, top: 0, height: 900 },
{ index: 1, top: 908, height: 900 },
],
anchor,
5000,
);
expect(restored).toBeCloseTo(1215.8);
expect(restored).not.toBe(908);
});
it('falls back to the previous scrollTop when the anchor page disappears', () => {
const restored = restoreScrollModeAnchor(
[{ index: 0, top: 0, height: 900 }],
{ index: 1, fraction: 0.4, scrollTop: 1350 },
1200,
);
expect(restored).toBe(1200);
});
});