refactor(ebook-reader): switch to CSS columns pagination approach
Replace HTML-splitting pagination with CSS columns for true paginated viewing. This simplifies the implementation and relies on the browser's native column-fill behavior for accurate page breaks. Changes: - view-modes.ts: Use CSS columns with column-fill: auto instead of pre-splitting HTML content into page chunks. Calculate page count from scrollHeight / viewportHeight. - reader-navigation.ts: Navigate by scrolling viewport height instead of extracting discrete page content. Track page position via scroll offset. Simplified renderSpineItem to load full spine content. - page-calculator.ts: Simplified to track spine info (charCount, estimatedPages) only. No more height-based content splitting. Page calculation happens in real-time from DOM scroll position. Benefits: - Accurate pagination without estimation errors - Works correctly across different font sizes and screen sizes - Simpler code with fewer edge cases - Natural page breaks at element boundaries via CSS
This commit is contained in:
@@ -72,15 +72,15 @@ function setViewMode(container: HTMLElement, mode: ViewMode): void {
|
||||
|
||||
function applyPaginatedMode(element: HTMLElement): void {
|
||||
element.classList.add("paginated");
|
||||
|
||||
// True pagination: content fits exactly in viewport, no scrolling
|
||||
element.style.height = "calc(100vh - 120px)"; // Account for chrome (top 60px + bottom 60px)
|
||||
element.style.overflow = "hidden";
|
||||
// Set up for CSS columns
|
||||
element.style.height = "calc(100vh - 120px)";
|
||||
element.style.overflowY = "auto";
|
||||
element.style.overflowX = "hidden";
|
||||
element.style.columnCount = "1";
|
||||
element.style.columnFill = "auto";
|
||||
element.style.columnGap = "0";
|
||||
element.style.position = "relative";
|
||||
|
||||
// Inject CSS for page clipping
|
||||
// Inject CSS for CSS column pagination
|
||||
injectPaginatedStyles();
|
||||
}
|
||||
|
||||
@@ -92,23 +92,22 @@ function injectPaginatedStyles(): void {
|
||||
const style = document.createElement("style");
|
||||
style.id = "paginated-styles";
|
||||
style.textContent = `
|
||||
.paginated .ebook-page {
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
.paginated {
|
||||
height: calc(100vh - 120px) !important;
|
||||
overflow-y: auto !important;
|
||||
overflow-x: hidden !important;
|
||||
column-fill: auto !important;
|
||||
column-count: 1 !important;
|
||||
column-gap: 0 !important;
|
||||
position: relative !important;
|
||||
}
|
||||
.paginated .ebook-page > * {
|
||||
max-height: 100%;
|
||||
overflow: hidden;
|
||||
.paginated > * {
|
||||
max-width: 100%;
|
||||
column-fill: auto;
|
||||
}
|
||||
.paginated .ebook-content {
|
||||
height: 100%;
|
||||
overflow: hidden !important;
|
||||
position: relative;
|
||||
}
|
||||
.paginated .ebook-content * {
|
||||
overflow-wrap: break-word;
|
||||
height: auto !important;
|
||||
min-height: 100%;
|
||||
}
|
||||
`;
|
||||
document.head.appendChild(style);
|
||||
@@ -144,29 +143,22 @@ function applyDoubleColumn(element: HTMLElement): void {
|
||||
function goToPage(container: HTMLElement, pageNumber: number): void {
|
||||
const content = container.querySelector(".ebook-content") as HTMLElement;
|
||||
if (!content) return;
|
||||
|
||||
const pageHeight = content.clientHeight;
|
||||
const scrollTop = (pageNumber - 1) * pageHeight;
|
||||
|
||||
const totalPages = getTotalPageCount(container);
|
||||
const targetPage = Math.min(Math.max(1, pageNumber), totalPages);
|
||||
const viewportHeight = window.innerHeight - 120;
|
||||
const scrollTop = (targetPage - 1) * viewportHeight;
|
||||
content.scrollTo({
|
||||
top: scrollTop,
|
||||
behavior: "smooth",
|
||||
});
|
||||
|
||||
const pageInfo = container.querySelector(".page-info");
|
||||
if (pageInfo) {
|
||||
pageInfo.textContent = `Page ${pageNumber} of ${getTotalPageCount(container)}`;
|
||||
}
|
||||
}
|
||||
|
||||
function getTotalPageCount(container: HTMLElement): number {
|
||||
const content = container.querySelector(".ebook-content") as HTMLElement;
|
||||
if (!content) return 1;
|
||||
|
||||
const totalHeight = content.scrollHeight;
|
||||
const pageHeight = content.clientHeight;
|
||||
|
||||
return Math.ceil(totalHeight / pageHeight);
|
||||
const viewportHeight = window.innerHeight - 120;
|
||||
const contentHeight = content.scrollHeight;
|
||||
return Math.max(1, Math.ceil(contentHeight / viewportHeight));
|
||||
}
|
||||
|
||||
export function getCurrentViewMode(container: HTMLElement): ViewMode {
|
||||
@@ -180,4 +172,3 @@ export function getCurrentViewMode(container: HTMLElement): ViewMode {
|
||||
|
||||
return "paginated";
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user