PDF zoom: use CSS zoom instead of re-render, abort stale renders
All checks were successful
Build and push Docker image / build (push) Successful in 12s
Test / test (push) Successful in 15s

- Add render generation counter (_pdfRenderGen) to abort overlapping renders
- Wrap all pages in #pdf-viewport div; zoom slider just updates style.zoom
- Use 'input' event on zoom slider for live preview without re-rendering
- enterPdfPaginatedMode resets viewport zoom to 1; exitPdfPaginatedMode restores it
- Remove pdfZoom factor from renderPdf scale (CSS zoom handles it now)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
marwin 2026-03-20 20:22:11 +01:00
parent 1cba67b3ed
commit 1026ed09a7
2 changed files with 26 additions and 4 deletions

View file

@ -1637,6 +1637,9 @@ body.dnd-mode .timer-display {
}
.reader-settings-panel input[type="range"] { width:80px; }
/* PDF viewport wrapper — CSS zoom applied here for smooth zoom without re-render */
#pdf-viewport { transform-origin: top left; }
/* PDF inner container — shares origin with canvas so text layer aligns exactly */
.pdf-page-inner { position:relative; display:inline-block; line-height:0; }

View file

@ -2594,6 +2594,7 @@ let readerSearchOpen = false;
let pdfCurrentPage = 1;
let pdfTotalPages = 0;
let _pdfPageTextBoxCache = {};
let _pdfRenderGen = 0;
let _touchStartX = 0;
if (typeof pdfjsLib !== 'undefined') {
@ -2836,7 +2837,9 @@ async function _parsePdfOutline(pdf, items, depth) {
}
async function renderPdf(arrayBuffer, contentEl, scaleOverride) {
const myGen = ++_pdfRenderGen;
const pdf = currentPdfDoc || await pdfjsLib.getDocument({data: new Uint8Array(arrayBuffer.slice(0))}).promise;
if (_pdfRenderGen !== myGen) return null;
currentPdfDoc = pdf;
let pdfTitle = '', pdfAuthor = '';
@ -2854,13 +2857,20 @@ async function renderPdf(arrayBuffer, contentEl, scaleOverride) {
contentEl.innerHTML = '';
// Viewport wrapper: CSS zoom controls display scale without re-rendering
const pdfVp = document.createElement('div');
pdfVp.id = 'pdf-viewport';
if (scaleOverride == null) pdfVp.style.zoom = readerSettings.pdfZoom / 100;
contentEl.appendChild(pdfVp);
const containerWidth = Math.min(contentEl.clientWidth - 32, 900);
for (let pageNum = 1; pageNum <= pdf.numPages; pageNum++) {
if (_pdfRenderGen !== myGen) { contentEl.innerHTML = ''; return null; }
const page = await pdf.getPage(pageNum);
const naturalVp = page.getViewport({scale: 1});
const scale = scaleOverride != null ? scaleOverride
: Math.max(0.5, (containerWidth / naturalVp.width) * (readerSettings.pdfZoom / 100));
: Math.max(0.5, containerWidth / naturalVp.width);
const viewport = page.getViewport({scale});
const wrapper = document.createElement('div');
@ -2878,7 +2888,7 @@ async function renderPdf(arrayBuffer, contentEl, scaleOverride) {
canvas.height = viewport.height;
inner.appendChild(canvas);
wrapper.appendChild(inner);
contentEl.appendChild(wrapper);
pdfVp.appendChild(wrapper);
await page.render({canvasContext: canvas.getContext('2d'), viewport}).promise;
@ -3424,11 +3434,16 @@ function toggleSettingsPanel() {
} else {
const zoomRange = panel.querySelector('#rs-zoom');
const zoomVal = panel.querySelector('#rs-zoom-val');
zoomRange.addEventListener('change', () => {
zoomRange.addEventListener('input', () => {
readerSettings.pdfZoom = parseInt(zoomRange.value, 10);
zoomVal.textContent = readerSettings.pdfZoom + '%';
saveReaderSettings();
reRenderPdf();
if (readerSettings.pdfPaginated) {
pdfSmartZoomPage(pdfCurrentPage);
} else {
const vp = document.getElementById('pdf-viewport');
if (vp) vp.style.zoom = readerSettings.pdfZoom / 100;
}
});
panel.querySelector('#rs-invert').addEventListener('click', function () {
@ -3469,6 +3484,8 @@ function enterPdfPaginatedMode() {
if (!contentEl) return;
contentEl.classList.add('pdf-paginated');
contentEl.style.overflow = 'hidden';
const pdfVp = document.getElementById('pdf-viewport');
if (pdfVp) pdfVp.style.zoom = 1;
const wrappers = contentEl.querySelectorAll('.pdf-page-wrapper');
wrappers.forEach((w, i) => {
@ -3493,6 +3510,8 @@ function exitPdfPaginatedMode() {
const canvas = w.querySelector('canvas');
if (canvas) canvas.style.transform = '';
});
const pdfVp = document.getElementById('pdf-viewport');
if (pdfVp) pdfVp.style.zoom = readerSettings.pdfZoom / 100;
}
function _pdfPaginatedClick(e) {