forked from akai/readest
d0071a6bcb
sync: empty-start/end range CFIs left by the cfi-inert skip-link bug (e.g. epubcfi(/6/24!/4,,/20/1:58)) resolve to a section-spanning range and navigate to the wrong end of the section. Add isMalformedLocationCfi and discard such locations on the cloud-sync receive path (useProgressSync) and the kosync push path (useKOSync) so they can't move the reader or propagate to other devices. foliate 569cc06 stops generating them but does not repair already-synced values. reader: bump foliate-js to 167757a to fix the white<->black background flash when swiping between differently-colored pages; add a regression test for the sliding per-view background segments. Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
78 lines
3.7 KiB
TypeScript
78 lines
3.7 KiB
TypeScript
// Regression test for the swipe page-turn background flash.
|
|
//
|
|
// In paginated mode each rendered view paints a full-bleed background segment
|
|
// positioned so it tracks its content on screen. The paginator rebuilds these
|
|
// on every scroll, so while the user drags a swipe the backgrounds stay glued
|
|
// to the content. The previous implementation painted evenly-spaced screen
|
|
// columns coloured by their midpoint, so a single colour spanned the whole
|
|
// viewport — mid-swipe, when two sections with different backgrounds were both
|
|
// visible, the incoming page rendered over the outgoing page's colour until the
|
|
// swipe snapped (e.g. dragging from a black page back to a white page showed the
|
|
// white page coming in black, then snapping white on release).
|
|
//
|
|
// See readest/readest swipe background flash.
|
|
import { describe, expect, it } from 'vitest';
|
|
|
|
import { computeBackgroundSegments } from 'foliate-js/paginator.js';
|
|
|
|
describe('computeBackgroundSegments', () => {
|
|
it('splits the background at the content seam mid-swipe (incoming page keeps its own colour)', () => {
|
|
// Single column, container coextensive with the full-bleed background.
|
|
// view0 is a transparent page (white via the host), view1 is a black page.
|
|
// Dragging from view1 back toward view0: scrolled 300 of 430 px.
|
|
const views = [
|
|
{ size: 430, bg: '' }, // page 1 — transparent, no segment
|
|
{ size: 430, bg: 'rgb(0, 0, 0)' }, // page 2 — black
|
|
];
|
|
const segments = computeBackgroundSegments(views, 300, 430, 0, 430);
|
|
|
|
// Only the black page produces a segment, and it starts at the content
|
|
// seam (430 - 300 = 130) — it must NOT bleed left over the incoming
|
|
// transparent page (which would render the white page black mid-swipe).
|
|
expect(segments).toEqual([{ start: 130, size: 300, bg: 'rgb(0, 0, 0)' }]);
|
|
expect(segments.every((s) => s.start >= 130)).toBe(true);
|
|
});
|
|
|
|
it('fills the whole screen at rest for a single colored page (full-bleed)', () => {
|
|
const views = [
|
|
{ size: 430, bg: 'rgb(0, 0, 0)' },
|
|
{ size: 430, bg: '' },
|
|
];
|
|
// At rest on the black page (scrollPos === its offset 0).
|
|
const segments = computeBackgroundSegments(views, 0, 430, 0, 430);
|
|
expect(segments).toEqual([{ start: 0, size: 430, bg: 'rgb(0, 0, 0)' }]);
|
|
});
|
|
|
|
it('stretches a centered page into the full-bleed gutters (inset > 0)', () => {
|
|
// Desktop-style: container is inset 50px inside a 430px background.
|
|
const views = [{ size: 330, bg: 'rgb(0, 0, 0)' }];
|
|
const segments = computeBackgroundSegments(views, 0, 430, 50, 330);
|
|
// Extends out to both background edges so the page is full-bleed.
|
|
expect(segments).toEqual([{ start: 0, size: 430, bg: 'rgb(0, 0, 0)' }]);
|
|
});
|
|
|
|
it('gives each section its own full-bleed half in a two-up spread', () => {
|
|
const views = [
|
|
{ size: 215, bg: 'rgb(255, 0, 0)' },
|
|
{ size: 215, bg: 'rgb(0, 0, 255)' },
|
|
];
|
|
// bg 500 wide, container 430 inset 35 → seam at 35 + 215 = 250.
|
|
const segments = computeBackgroundSegments(views, 0, 500, 35, 430);
|
|
expect(segments).toEqual([
|
|
{ start: 0, size: 250, bg: 'rgb(255, 0, 0)' }, // left page bleeds into left gutter
|
|
{ start: 250, size: 250, bg: 'rgb(0, 0, 255)' }, // right page bleeds into right gutter
|
|
]);
|
|
});
|
|
|
|
it('skips views scrolled far off screen', () => {
|
|
const views = [
|
|
{ size: 430, bg: 'rgb(0, 0, 0)' }, // offset 0 — far left, fully gone
|
|
{ size: 430, bg: 'rgb(1, 1, 1)' }, // offset 430
|
|
{ size: 430, bg: 'rgb(2, 2, 2)' }, // offset 860 — far right, beyond headroom
|
|
];
|
|
// Scrolled to the middle view.
|
|
const segments = computeBackgroundSegments(views, 430, 430, 0, 430);
|
|
expect(segments).toEqual([{ start: 0, size: 430, bg: 'rgb(1, 1, 1)' }]);
|
|
});
|
|
});
|