mirror of
https://github.com/john-okeefe/foliate-js.git
synced 2026-09-09 11:29:14 -04:00
Fix zoom percentage to track relative to fit-page scale
The zoom percentage display had two bugs:
1. Scroll-zoom never updated the status bar percentage because the
'zoom' event listener was registered inside the magnifier-toggle
click handler (only added on click, and re-added on each click).
2. After reset (fit-page), the status bar showed '100%' but the
zoom-in button treated the current scale as 1.0 (absolute), not
the fit-page scale (e.g. 0.3). This caused a wild zoom jump
from '100%' to '120%' that was actually a 4x visual change.
Fix by introducing a #baseScale field that captures the fit-page or
fit-width scale whenever the component renders in auto-fit mode. The
new currentScale getter exposes the actual transform scale, and
zoomPercent expresses it as a percentage of baseScale. All zoom-in/
zoom-out handlers (buttons and keyboard) now use currentScale instead
of getAttribute('zoom') || 1, and all percentage displays use
zoomPercent. The zoom event listener is registered once at setup
rather than inside the magnifier toggle handler.
Also removes all diagnostic console.log statements from fixed-layout.js
and reader.js.
This commit is contained in:
+13
-31
@@ -40,6 +40,7 @@ export class FixedLayout extends HTMLElement {
|
|||||||
#interactionMode = "select";
|
#interactionMode = "select";
|
||||||
#wrapper;
|
#wrapper;
|
||||||
#transform = { x: 0, y: 0, scale: 1 };
|
#transform = { x: 0, y: 0, scale: 1 };
|
||||||
|
#baseScale = 1;
|
||||||
#zoomState = {
|
#zoomState = {
|
||||||
minScale: 0.1,
|
minScale: 0.1,
|
||||||
maxScale: 10,
|
maxScale: 10,
|
||||||
@@ -132,6 +133,14 @@ export class FixedLayout extends HTMLElement {
|
|||||||
return this.#magnifier.enabled;
|
return this.#magnifier.enabled;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
get currentScale() {
|
||||||
|
return this.#transform.scale;
|
||||||
|
}
|
||||||
|
|
||||||
|
get zoomPercent() {
|
||||||
|
return Math.round((this.#transform.scale / this.#baseScale) * 100);
|
||||||
|
}
|
||||||
|
|
||||||
resetZoom() {
|
resetZoom() {
|
||||||
this.#zoom = undefined;
|
this.#zoom = undefined;
|
||||||
this.#render();
|
this.#render();
|
||||||
@@ -395,14 +404,6 @@ export class FixedLayout extends HTMLElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#zoomByRatio(cx, cy, ratio) {
|
#zoomByRatio(cx, cy, ratio) {
|
||||||
console.log("[FXL] zoomByRatio:", {
|
|
||||||
cx,
|
|
||||||
cy,
|
|
||||||
ratio,
|
|
||||||
oldScale: this.#transform.scale,
|
|
||||||
oldX: this.#transform.x,
|
|
||||||
oldY: this.#transform.y,
|
|
||||||
});
|
|
||||||
const oldScale = this.#transform.scale;
|
const oldScale = this.#transform.scale;
|
||||||
const newScale = Math.min(
|
const newScale = Math.min(
|
||||||
this.#zoomState.maxScale,
|
this.#zoomState.maxScale,
|
||||||
@@ -421,12 +422,6 @@ export class FixedLayout extends HTMLElement {
|
|||||||
this.dispatchEvent(
|
this.dispatchEvent(
|
||||||
new CustomEvent("zoom", { detail: { scale: newScale } }),
|
new CustomEvent("zoom", { detail: { scale: newScale } }),
|
||||||
);
|
);
|
||||||
console.log("[FXL] zoomByRatio result:", {
|
|
||||||
newX: this.#transform.x,
|
|
||||||
newY: this.#transform.y,
|
|
||||||
newScale: this.#transform.scale,
|
|
||||||
actualRatio,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#handleWheel(event) {
|
#handleWheel(event) {
|
||||||
@@ -442,16 +437,6 @@ export class FixedLayout extends HTMLElement {
|
|||||||
event.deltaY > 0
|
event.deltaY > 0
|
||||||
? 1 - this.#zoomState.zoomStep
|
? 1 - this.#zoomState.zoomStep
|
||||||
: 1 + this.#zoomState.zoomStep;
|
: 1 + this.#zoomState.zoomStep;
|
||||||
console.log("[FXL] wheel:", {
|
|
||||||
cx,
|
|
||||||
cy,
|
|
||||||
transformX: this.#transform.x,
|
|
||||||
transformY: this.#transform.y,
|
|
||||||
transformScale: this.#transform.scale,
|
|
||||||
zoom: this.#zoom,
|
|
||||||
side: this.#side,
|
|
||||||
hostWidth: rect.width,
|
|
||||||
});
|
|
||||||
this.#zoomByRatio(cx, cy, ratio);
|
this.#zoomByRatio(cx, cy, ratio);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -656,13 +641,6 @@ export class FixedLayout extends HTMLElement {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#render(side = this.#side) {
|
#render(side = this.#side) {
|
||||||
console.log("[FXL] render:", {
|
|
||||||
side,
|
|
||||||
zoom: this.#zoom,
|
|
||||||
transformScale: this.#transform.scale,
|
|
||||||
isNumericZoom: typeof this.#zoom === "number" && !isNaN(this.#zoom),
|
|
||||||
hasDualFrames: !this.#center && !this.#left?.blank && !this.#right?.blank,
|
|
||||||
});
|
|
||||||
if (!side) return;
|
if (!side) return;
|
||||||
const left = this.#left ?? {};
|
const left = this.#left ?? {};
|
||||||
const right = this.#center ?? this.#right ?? {};
|
const right = this.#center ?? this.#right ?? {};
|
||||||
@@ -699,6 +677,10 @@ export class FixedLayout extends HTMLElement {
|
|||||||
|
|
||||||
this.#transform.scale = scale;
|
this.#transform.scale = scale;
|
||||||
|
|
||||||
|
if (typeof this.#zoom !== "number" || isNaN(this.#zoom)) {
|
||||||
|
this.#baseScale = scale;
|
||||||
|
}
|
||||||
|
|
||||||
this.#updateFrameScales(scale);
|
this.#updateFrameScales(scale);
|
||||||
|
|
||||||
const { contentWidth, contentHeight } = this.#getContentSize();
|
const { contentWidth, contentHeight } = this.#getContentSize();
|
||||||
|
|||||||
@@ -120,19 +120,8 @@ class Reader {
|
|||||||
|
|
||||||
const { book } = this.view;
|
const { book } = this.view;
|
||||||
|
|
||||||
// DEBUG: Log book and renderer info
|
|
||||||
console.log("[Reader Debug] 📖 Book opened:", {
|
|
||||||
title: book.metadata?.title,
|
|
||||||
type: file.name,
|
|
||||||
rendition: book.rendition,
|
|
||||||
isFixedLayout: this.view.isFixedLayout,
|
|
||||||
renderer: this.view.renderer?.localName,
|
|
||||||
rendererClass: this.view.renderer?.constructor.name,
|
|
||||||
isFixedLayoutInstance: this.view.renderer instanceof FixedLayout,
|
|
||||||
});
|
|
||||||
book.transformTarget?.addEventListener("data", ({ detail }) => {
|
book.transformTarget?.addEventListener("data", ({ detail }) => {
|
||||||
detail.data = Promise.resolve(detail.data).catch((e) => {
|
detail.data = Promise.resolve(detail.data).catch(() => {
|
||||||
console.error(new Error(`Failed to load ${detail.name}`, { cause: e }));
|
|
||||||
return "";
|
return "";
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -156,83 +145,44 @@ class Reader {
|
|||||||
}
|
}
|
||||||
|
|
||||||
document.addEventListener("keydown", this.#handleKeydown.bind(this));
|
document.addEventListener("keydown", this.#handleKeydown.bind(this));
|
||||||
console.log("[Reader Debug] 🔧 Setting up zoom control listeners...");
|
|
||||||
|
|
||||||
// Add zoom control listeners
|
// Add zoom control listeners
|
||||||
$("#zoom-in").addEventListener("click", () => {
|
$("#zoom-in").addEventListener("click", () => {
|
||||||
console.log("[Reader Debug] 🔍 Zoom in button clicked");
|
|
||||||
const renderer = this.view.renderer;
|
const renderer = this.view.renderer;
|
||||||
console.log(
|
|
||||||
"[Reader Debug] Renderer:",
|
|
||||||
renderer?.localName,
|
|
||||||
renderer?.constructor.name,
|
|
||||||
);
|
|
||||||
console.log(
|
|
||||||
"[Reader Debug] Is FixedLayout?",
|
|
||||||
renderer instanceof FixedLayout,
|
|
||||||
);
|
|
||||||
|
|
||||||
if (renderer instanceof FixedLayout) {
|
if (renderer instanceof FixedLayout) {
|
||||||
const current = renderer.getAttribute("zoom") || 1;
|
const newScale = Math.min(10, renderer.currentScale * 1.2);
|
||||||
const newZoom = Math.min(10, parseFloat(current) * 1.2);
|
renderer.setAttribute("zoom", newScale);
|
||||||
renderer.setAttribute("zoom", newZoom);
|
$("#zoom-reset").textContent = `${renderer.zoomPercent}%`;
|
||||||
$("#zoom-reset").textContent = `${Math.round(newZoom * 100)}%`;
|
|
||||||
console.log("[Reader Debug] ✅ Zoom applied:", current, "->", newZoom);
|
|
||||||
} else {
|
|
||||||
console.log("[Reader Debug] ❌ Not a FixedLayout renderer, ignoring");
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
$("#zoom-out").addEventListener("click", () => {
|
$("#zoom-out").addEventListener("click", () => {
|
||||||
console.log("[Reader Debug] 🔍 Zoom out button clicked");
|
|
||||||
const renderer = this.view.renderer;
|
const renderer = this.view.renderer;
|
||||||
console.log(
|
|
||||||
"[Reader Debug] Is FixedLayout?",
|
|
||||||
renderer instanceof FixedLayout,
|
|
||||||
);
|
|
||||||
if (renderer instanceof FixedLayout) {
|
if (renderer instanceof FixedLayout) {
|
||||||
const current = renderer.getAttribute("zoom") || 1;
|
const newScale = Math.max(0.1, renderer.currentScale / 1.2);
|
||||||
const newZoom = Math.max(0.1, parseFloat(current) / 1.2);
|
renderer.setAttribute("zoom", newScale);
|
||||||
renderer.setAttribute("zoom", newZoom);
|
$("#zoom-reset").textContent = `${renderer.zoomPercent}%`;
|
||||||
$("#zoom-reset").textContent = `${Math.round(newZoom * 100)}%`;
|
|
||||||
console.log("[Reader Debug] ✅ Zoom applied:", current, "->", newZoom);
|
|
||||||
} else {
|
|
||||||
console.log("[Reader Debug] ❌ Not a FixedLayout renderer, ignoring");
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
$("#zoom-reset").addEventListener("click", () => {
|
$("#zoom-reset").addEventListener("click", () => {
|
||||||
console.log("[Reader Debug] 🔄 Reset zoom button clicked");
|
|
||||||
const renderer = this.view.renderer;
|
const renderer = this.view.renderer;
|
||||||
console.log(
|
|
||||||
"[Reader Debug] Is FixedLayout?",
|
|
||||||
renderer instanceof FixedLayout,
|
|
||||||
);
|
|
||||||
if (renderer instanceof FixedLayout) {
|
if (renderer instanceof FixedLayout) {
|
||||||
renderer.resetZoom();
|
renderer.resetZoom();
|
||||||
renderer.dragOffset = { x: 0, y: 0 };
|
renderer.dragOffset = { x: 0, y: 0 };
|
||||||
$("#zoom-reset").textContent = "100%";
|
$("#zoom-reset").textContent = "100%";
|
||||||
console.log("[Reader Debug] ✅ Zoom reset");
|
|
||||||
} else {
|
|
||||||
console.log("[Reader Debug] ❌ Not a FixedLayout renderer, ignoring");
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
$("#magnifier-toggle").addEventListener("click", () => {
|
|
||||||
console.log("[Reader Debug] 🔍 Magnifier toggle button clicked");
|
|
||||||
const renderer = this.view.renderer;
|
const renderer = this.view.renderer;
|
||||||
renderer.addEventListener("zoom", (e) => {
|
renderer.addEventListener("zoom", () => {
|
||||||
$("#zoom-reset").textContent = `${Math.round(e.detail.scale * 100)}%`;
|
$("#zoom-reset").textContent = `${renderer.zoomPercent}%`;
|
||||||
});
|
});
|
||||||
console.log(
|
$("#magnifier-toggle").addEventListener("click", () => {
|
||||||
"[Reader Debug] Is FixedLayout?",
|
const renderer = this.view.renderer;
|
||||||
renderer instanceof FixedLayout,
|
|
||||||
);
|
|
||||||
if (renderer instanceof FixedLayout) {
|
if (renderer instanceof FixedLayout) {
|
||||||
renderer.toggleMagnifier();
|
renderer.toggleMagnifier();
|
||||||
console.log("[Reader Debug] ✅ Magnifier toggled");
|
|
||||||
} else {
|
|
||||||
console.log("[Reader Debug] ❌ Not a FixedLayout renderer, ignoring");
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
const modeToggle = $("#interaction-mode-toggle");
|
const modeToggle = $("#interaction-mode-toggle");
|
||||||
@@ -255,7 +205,6 @@ class Reader {
|
|||||||
modeToggle.style.display = "";
|
modeToggle.style.display = "";
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
console.log("[Reader Debug] ✅ Zoom control listeners configured");
|
|
||||||
|
|
||||||
const title = formatLanguageMap(book.metadata?.title) || "Untitled Book";
|
const title = formatLanguageMap(book.metadata?.title) || "Untitled Book";
|
||||||
document.title = title;
|
document.title = title;
|
||||||
@@ -309,47 +258,28 @@ class Reader {
|
|||||||
}
|
}
|
||||||
#handleKeydown(event) {
|
#handleKeydown(event) {
|
||||||
const k = event.key;
|
const k = event.key;
|
||||||
console.log("[Reader Debug] ⌨️ Key pressed:", k);
|
|
||||||
if (k === "ArrowLeft" || k === "h") this.view.goLeft();
|
if (k === "ArrowLeft" || k === "h") this.view.goLeft();
|
||||||
else if (k === "ArrowRight" || k === "l") this.view.goRight();
|
else if (k === "ArrowRight" || k === "l") this.view.goRight();
|
||||||
else if (k === "+" || k === "=") {
|
else if (k === "+" || k === "=") {
|
||||||
console.log("[Reader Debug] 🔍 Zoom in via keyboard");
|
|
||||||
const renderer = this.view.renderer;
|
const renderer = this.view.renderer;
|
||||||
if (renderer instanceof FixedLayout) {
|
if (renderer instanceof FixedLayout) {
|
||||||
const current = renderer.getAttribute("zoom") || 1;
|
const newScale = Math.min(10, renderer.currentScale * 1.2);
|
||||||
const newZoom = Math.min(10, parseFloat(current) * 1.2);
|
renderer.setAttribute("zoom", newScale);
|
||||||
renderer.setAttribute("zoom", newZoom);
|
$("#zoom-reset").textContent = `${renderer.zoomPercent}%`;
|
||||||
$("#zoom-reset").textContent = `${Math.round(newZoom * 100)}%`;
|
|
||||||
console.log(
|
|
||||||
"[Reader Debug] ✅ Keyboard zoom applied:",
|
|
||||||
current,
|
|
||||||
"->",
|
|
||||||
newZoom,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
} else if (k === "-" || k === "_") {
|
} else if (k === "-" || k === "_") {
|
||||||
console.log("[Reader Debug] 🔍 Zoom out via keyboard");
|
|
||||||
const renderer = this.view.renderer;
|
const renderer = this.view.renderer;
|
||||||
if (renderer instanceof FixedLayout) {
|
if (renderer instanceof FixedLayout) {
|
||||||
const current = renderer.getAttribute("zoom") || 1;
|
const newScale = Math.max(0.1, renderer.currentScale / 1.2);
|
||||||
const newZoom = Math.max(0.1, parseFloat(current) / 1.2);
|
renderer.setAttribute("zoom", newScale);
|
||||||
renderer.setAttribute("zoom", newZoom);
|
$("#zoom-reset").textContent = `${renderer.zoomPercent}%`;
|
||||||
$("#zoom-reset").textContent = `${Math.round(newZoom * 100)}%`;
|
|
||||||
console.log(
|
|
||||||
"[Reader Debug] ✅ Keyboard zoom applied:",
|
|
||||||
current,
|
|
||||||
"->",
|
|
||||||
newZoom,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
} else if (k === "0") {
|
} else if (k === "0") {
|
||||||
console.log("[Reader Debug] 🔄 Reset zoom via keyboard");
|
|
||||||
const renderer = this.view.renderer;
|
const renderer = this.view.renderer;
|
||||||
if (renderer instanceof FixedLayout) {
|
if (renderer instanceof FixedLayout) {
|
||||||
renderer.resetZoom();
|
renderer.resetZoom();
|
||||||
renderer.dragOffset = { x: 0, y: 0 };
|
renderer.dragOffset = { x: 0, y: 0 };
|
||||||
$("#zoom-reset").textContent = "100%";
|
$("#zoom-reset").textContent = "100%";
|
||||||
console.log("[Reader Debug] ✅ Keyboard zoom reset");
|
|
||||||
}
|
}
|
||||||
} else if (k === "Escape") {
|
} else if (k === "Escape") {
|
||||||
const renderer = this.view.renderer;
|
const renderer = this.view.renderer;
|
||||||
|
|||||||
Reference in New Issue
Block a user