Render PDF from current page outward on zoom/re-render
renderPdf now takes an optional pivotPage. Pass 1 builds the full DOM and sizes all canvases (instant). Then scrolls to the pivot page immediately so the user stays in place. Pass 2 renders pixels from pivot→end, then pivot-1→start. reRenderPdf detects the current visible page before re-rendering and passes it as pivot, so zoom no longer resets scroll position. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
20a1b9a889
commit
1cf3f730ea
1 changed files with 38 additions and 22 deletions
|
|
@ -3010,7 +3010,7 @@ async function _parsePdfOutline(pdf, items, depth) {
|
|||
return result;
|
||||
}
|
||||
|
||||
async function renderPdf(arrayBuffer, contentEl, scaleOverride) {
|
||||
async function renderPdf(arrayBuffer, contentEl, scaleOverride, pivotPage) {
|
||||
const myGen = ++_pdfRenderGen;
|
||||
const pdf = currentPdfDoc || await pdfjsLib.getDocument({data: new Uint8Array(arrayBuffer.slice(0))}).promise;
|
||||
if (_pdfRenderGen !== myGen) return null;
|
||||
|
|
@ -3031,7 +3031,6 @@ 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';
|
||||
contentEl.appendChild(pdfVp);
|
||||
|
|
@ -3039,15 +3038,17 @@ async function renderPdf(arrayBuffer, contentEl, scaleOverride) {
|
|||
const containerWidth = readerSettings.pdfSpread
|
||||
? contentEl.clientWidth - 32
|
||||
: Math.min(contentEl.clientWidth - 32, 900);
|
||||
const pageWidth = readerSettings.pdfSpread ? (containerWidth - 8) / 2 : containerWidth;
|
||||
const dpr = window.devicePixelRatio || 1;
|
||||
|
||||
// Pass 1: build DOM structure and size all canvases (no pixel rendering yet)
|
||||
const pageEntries = [];
|
||||
let spreadContainer = null;
|
||||
|
||||
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 pageWidth = readerSettings.pdfSpread ? (containerWidth - 8) / 2 : containerWidth;
|
||||
// Zoom baked into canvas resolution — no CSS zoom, stays sharp at any DPR
|
||||
const scale = scaleOverride != null ? scaleOverride
|
||||
: Math.max(0.5, pageWidth / naturalVp.width) * (readerSettings.pdfZoom / 100);
|
||||
const viewport = page.getViewport({scale});
|
||||
|
|
@ -3056,12 +3057,9 @@ async function renderPdf(arrayBuffer, contentEl, scaleOverride) {
|
|||
wrapper.className = 'pdf-page-wrapper';
|
||||
wrapper.id = `pdf-page-${pageNum}`;
|
||||
|
||||
// Inner container gives canvas + text layer a shared position:relative origin,
|
||||
// independent of the outer flex wrapper's centering.
|
||||
const inner = document.createElement('div');
|
||||
inner.className = 'pdf-page-inner';
|
||||
|
||||
const dpr = window.devicePixelRatio || 1;
|
||||
const canvas = document.createElement('canvas');
|
||||
canvas.className = 'pdf-page';
|
||||
canvas.width = Math.round(viewport.width * dpr);
|
||||
|
|
@ -3087,14 +3085,29 @@ async function renderPdf(arrayBuffer, contentEl, scaleOverride) {
|
|||
else pdfVp.appendChild(wrapper);
|
||||
}
|
||||
|
||||
const ctx = canvas.getContext('2d');
|
||||
ctx.scale(dpr, dpr);
|
||||
await page.render({canvasContext: ctx, viewport}).promise;
|
||||
|
||||
// Text layer disabled — re-enable once overlay rendering is resolved
|
||||
pageEntries.push({page, canvas, viewport});
|
||||
}
|
||||
|
||||
pdfTotalPages = pdf.numPages;
|
||||
|
||||
// Scroll to pivot page immediately — DOM and canvas sizes are set
|
||||
const pivot = Math.max(1, Math.min(pdf.numPages, pivotPage || 1));
|
||||
const pivotEl = document.getElementById(`pdf-page-${pivot}`);
|
||||
if (pivotEl && pivot > 1) pivotEl.scrollIntoView({block: 'start'});
|
||||
|
||||
// Pass 2: render pixels from pivot outward (pivot→end, then pivot-1→start)
|
||||
const order = [];
|
||||
for (let i = pivot - 1; i < pdf.numPages; i++) order.push(i);
|
||||
for (let i = pivot - 2; i >= 0; i--) order.push(i);
|
||||
|
||||
for (const idx of order) {
|
||||
if (_pdfRenderGen !== myGen) { contentEl.innerHTML = ''; return null; }
|
||||
const {page, canvas, viewport} = pageEntries[idx];
|
||||
const ctx = canvas.getContext('2d');
|
||||
ctx.scale(dpr, dpr);
|
||||
await page.render({canvasContext: ctx, viewport}).promise;
|
||||
}
|
||||
|
||||
return {title: pdfTitle, author: pdfAuthor, toc, numPages: pdf.numPages};
|
||||
}
|
||||
|
||||
|
|
@ -3698,10 +3711,6 @@ function toggleSettingsPanel() {
|
|||
}
|
||||
|
||||
async function applyPdfZoom(newZoom) {
|
||||
const contentEl2 = $('reader-content');
|
||||
const fraction = contentEl2
|
||||
? contentEl2.scrollTop / (contentEl2.scrollHeight - contentEl2.clientHeight || 1)
|
||||
: 0;
|
||||
readerSettings.pdfZoom = newZoom;
|
||||
const zoomRange = document.getElementById('rs-zoom');
|
||||
const zoomVal = document.getElementById('rs-zoom-val');
|
||||
|
|
@ -3712,25 +3721,32 @@ async function applyPdfZoom(newZoom) {
|
|||
pdfSmartZoomPage(pdfCurrentPage);
|
||||
} else {
|
||||
await reRenderPdf();
|
||||
// Wait for two animation frames so the browser finishes layout
|
||||
// before reading scrollHeight for position restoration
|
||||
await new Promise(r => requestAnimationFrame(() => requestAnimationFrame(r)));
|
||||
if (contentEl2 && fraction > 0) {
|
||||
contentEl2.scrollTop = fraction * (contentEl2.scrollHeight - contentEl2.clientHeight);
|
||||
}
|
||||
}
|
||||
|
||||
function _currentScrollPage(contentEl) {
|
||||
const wrappers = contentEl.querySelectorAll('.pdf-page-wrapper');
|
||||
const viewTop = contentEl.scrollTop;
|
||||
for (const w of wrappers) {
|
||||
if (w.offsetTop + w.offsetHeight > viewTop) {
|
||||
const n = parseInt(w.id.replace('pdf-page-', ''), 10);
|
||||
if (n) return n;
|
||||
}
|
||||
}
|
||||
return pdfCurrentPage || 1;
|
||||
}
|
||||
|
||||
async function reRenderPdf() {
|
||||
if (!currentPdfBuffer) return;
|
||||
const contentEl = $('reader-content');
|
||||
if (!contentEl) return;
|
||||
const pivot = readerSettings.pdfPaginated ? pdfCurrentPage : _currentScrollPage(contentEl);
|
||||
const overlay = $('reader-overlay');
|
||||
const loadingEl = document.createElement('div');
|
||||
loadingEl.className = 'pdf-loading-overlay';
|
||||
loadingEl.innerHTML = '<span class="pdf-loading-spinner"></span>';
|
||||
if (overlay) overlay.appendChild(loadingEl);
|
||||
await renderPdf(currentPdfBuffer, contentEl);
|
||||
await renderPdf(currentPdfBuffer, contentEl, undefined, pivot);
|
||||
loadingEl.remove();
|
||||
if (readerSettings.pdfPaginated) enterPdfPaginatedMode();
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue