From c1d0b5f7daa3aa2684b8179133790e216c6b82e6 Mon Sep 17 00:00:00 2001 From: elixir <1597046542@qq.com> Date: Mon, 20 Jul 2026 16:12:22 +0800 Subject: [PATCH 1/2] fix(epubjs): preserve last page at chapter boundaries --- packages/epubjs/src/managers/default/index.js | 30 ++++++++++++++----- 1 file changed, 22 insertions(+), 8 deletions(-) diff --git a/packages/epubjs/src/managers/default/index.js b/packages/epubjs/src/managers/default/index.js index cbc0311c..91d20d63 100644 --- a/packages/epubjs/src/managers/default/index.js +++ b/packages/epubjs/src/managers/default/index.js @@ -481,13 +481,24 @@ class DefaultViewManager { ) { this.scrollLeft = this.container.scrollLeft - left = - this.container.scrollLeft + - this.container.offsetWidth + - this.layout.delta - - if (left <= this.container.scrollWidth) { - this.scrollBy(this.layout.delta, 0, true) + // `offsetWidth` includes borders while `scrollWidth` and `clientWidth` + // describe the inner scrollable area. Mixing them can make the last + // page unreachable because of rounding or border-width differences. + const maxScrollLeft = Math.max( + 0, + this.container.scrollWidth - this.container.clientWidth, + ) + const epsilon = 1 + + if (this.container.scrollLeft < maxScrollLeft - epsilon) { + this.scrollTo( + Math.min( + this.container.scrollLeft + this.layout.delta, + maxScrollLeft, + ), + 0, + true, + ) } else { next = this.views.last().section.next() } @@ -675,7 +686,10 @@ class DefaultViewManager { } } else { this.scrollTo( - this.container.scrollWidth - this.layout.delta, + Math.max( + 0, + this.container.scrollWidth - this.container.clientWidth, + ), 0, true, ) From 78054c59cec79ff0d658249983be614ca4d47817 Mon Sep 17 00:00:00 2001 From: elixir <1597046542@qq.com> Date: Mon, 20 Jul 2026 16:34:14 +0800 Subject: [PATCH 2/2] fix(epubjs): snap paginated navigation to page boundaries --- packages/epubjs/src/managers/default/index.js | 21 ++++++++++++------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/packages/epubjs/src/managers/default/index.js b/packages/epubjs/src/managers/default/index.js index 91d20d63..6406c560 100644 --- a/packages/epubjs/src/managers/default/index.js +++ b/packages/epubjs/src/managers/default/index.js @@ -489,13 +489,13 @@ class DefaultViewManager { this.container.scrollWidth - this.container.clientWidth, ) const epsilon = 1 + const currentPage = Math.round( + this.container.scrollLeft / this.layout.delta, + ) if (this.container.scrollLeft < maxScrollLeft - epsilon) { this.scrollTo( - Math.min( - this.container.scrollLeft + this.layout.delta, - maxScrollLeft, - ), + Math.min((currentPage + 1) * this.layout.delta, maxScrollLeft), 0, true, ) @@ -596,8 +596,9 @@ class DefaultViewManager { left = this.container.scrollLeft - if (left > 0) { - this.scrollBy(-this.layout.delta, 0, true) + const currentPage = Math.round(left / this.layout.delta) + if (currentPage > 0) { + this.scrollTo((currentPage - 1) * this.layout.delta, 0, true) } else { prev = this.views.first().section.prev() } @@ -853,9 +854,13 @@ class DefaultViewManager { ) let totalPages = this.layout.count(width).pages - let startPage = Math.floor(start / this.layout.pageWidth) + // Treat sub-pixel and border rounding near a page boundary as that page. + const positionEpsilon = 1 + let startPage = Math.floor( + (start + positionEpsilon) / this.layout.pageWidth, + ) let pages = [] - let endPage = Math.floor(end / this.layout.pageWidth) + let endPage = Math.floor((end + positionEpsilon) / this.layout.pageWidth) // start page should not be negative if (startPage < 0) {