From f622841e29759a6845c434e0823217b4163655a1 Mon Sep 17 00:00:00 2001 From: John O'Keefe Date: Thu, 15 Oct 2026 13:29:30 -0400 Subject: [PATCH] =?UTF-8?q?feat(paginator):=20manual=20touch=20scrolling?= =?UTF-8?q?=20in=20scrolled=20layouts=20=E2=80=94=20kill=20the=20native=20?= =?UTF-8?q?fling?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Scrolled (webtoon) layouts now scroll single-finger drags manually, the same path paginated layouts always used: the browser's native touch scrolling — and its fling momentum — is suppressed so the full gesture stream stays observable to hosts. The reader's chrome UX (bookhoard handoff item 7) classifies vertical flicks from that stream: flick-down reveals, down/up dismisses. Side-swipes in paginated layouts and drags in scrolled ones both page/scroll exactly as before, minus momentum. Release behavior in scrolled layouts is stop-where-released — no snap, deliberately: webtoon panels don't align to screen boundaries and a snap would fight zoomed panning. Long jumps stay the position UI's job. Pinch zoom, multi-touch, and active touch selection keep their native behavior via the existing guards (they return before the preventDefault). Desktop wheel scrolling is untouched. Version 0.5.4. --- package.json | 2 +- paginator.js | 14 ++++++++++++-- 2 files changed, 13 insertions(+), 3 deletions(-) diff --git a/package.json b/package.json index 90b5625..cc8d5de 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "foliate-js", - "version": "0.5.3", + "version": "0.5.4", "description": "Render e-books in the browser", "repository": { "type": "git", diff --git a/paginator.js b/paginator.js index f2389eb..f246d2f 100644 --- a/paginator.js +++ b/paginator.js @@ -986,7 +986,14 @@ export class Paginator extends HTMLElement { const state = this.#touchState if (state.pinched) return state.pinched = globalThis.visualViewport.scale > 1 - if (this.scrolled || state.pinched) return + // Scrolled layouts (webtoon) scroll MANUALLY, like paginated ones: + // the browser's native touch scrolling — and its fling momentum — + // is suppressed so the full gesture stream stays observable to + // hosts (the reader classifies vertical flicks as chrome-reveal). + // Momentum is deliberately absent: drags are 1:1 and stop dead on + // release (no snap — webtoon panels don't align to screen + // boundaries); long jumps go through the reader's position UI. + if (state.pinched) return if (this.#touchSelActive) return if (e.touches.length > 1) { if (this.#touchScrolled) e.preventDefault() @@ -1008,11 +1015,14 @@ export class Paginator extends HTMLElement { #onTouchEnd() { const wasScrolled = this.#touchScrolled this.#touchScrolled = false - if (this.scrolled || this.#touchSelActive) return + if (this.#touchSelActive) return // A touch that never panned (a tap) is already page-aligned; // snapping anyway emits a no-op relocate, which readers treat // as a page turn (e.g. dismissing a just-opened popover). if (!wasScrolled) return + // No velocity paging in scrolled layouts: content stays exactly + // where released. + if (this.scrolled) return // XXX: Firefox seems to report scale as 1... sometimes...? // at this point I'm basically throwing `requestAnimationFrame` at