mirror of
https://github.com/yu-i-i/overleaf-cep.git
synced 2026-05-23 09:09:36 +02:00
Wrap PDF setDocument in startViewTransition (#33346)
* Set scale synchronously on pagesinit to prevent 1.333 DPI flash
PDF.js resets its internal scale to 1.0 when setDocument() is called,
causing pages to momentarily render at the default 96/72 DPI scale
(1.333) before the React restore effect can apply the correct value.
Setting currentScaleValue directly in the pagesinit handler eliminates
this one-frame wrong-scale flash.
* Override .page display to block to prevent horizontal jump on recompile
Overleaf's global .loading class sets display:inline-flex, which
collides with PDF.js's transient 'loading' class on .page elements.
When the loading class is applied, inline-flex breaks margin:auto
centering, causing the page to jump horizontally. Forcing display:block
at higher specificity prevents the global rule from taking effect.
* Fix scrollToPosition offset using marginTop instead of borderWidth
scrollPageIntoView aligns the page content edge with the container top,
leaving scrollTop equal to the page's top margin (12px) rather than 0.
The previous correction used borderWidth (effectively 0) so the margin
offset was never compensated. Using marginTop scrolls back the correct
amount so the margin above the first page is visible.
* Prevent PDF viewer collapsing during recompile by preserving height
When setDocument() is called with a new PDF, _resetView() synchronously
clears all page elements, briefly collapsing the .pdfViewer div to the
viewport height. This produces a visible flicker before pagesinit fires
and pages are re-added.
Fix: record the current height and pin it as min-height on the .pdfViewer
element before calling setDocument(). A one-shot pagesinit listener
removes the constraint once the new pages are initialised at the correct
scale, by which point the element is already at its correct final height.
* Suppress PDF.js page-level loading spinner in Overleaf viewer
The PDF.js loadingIcon/loading classes briefly add a ::after pseudo-element
with display:block and contain:strict to each page div. Overleaf has its
own loading state UI so the spinner is redundant, and its activation was
the root cause of the shifts 4-5 height oscillation (the display change
broke CSS margin collapse on .pdfViewer, adding 2x page margins to its
computed height).
The display:block rule already added to .page prevents the direct cause
(Overleaf's .loading{display:inline-flex} colliding with the PDF.js class).
This rule makes the intent explicit by zeroing the ::after entirely.
* Wrap PDF setDocument in startViewTransition
---------
Co-authored-by: Brian Gough <brian.gough@overleaf.com>
GitOrigin-RevId: 353ab865de3c7872363a61592d86390dfc34dacc
This commit is contained in:
@@ -114,6 +114,9 @@ function PdfJsViewer({ url, pdfFile }: PdfJsViewerProps) {
|
||||
}
|
||||
|
||||
const handlePagesinit = () => {
|
||||
// Set scale immediately to avoid a one-frame flash at PDF.js's default
|
||||
// 1.333 scale (96/72 DPI) before the React restore effect can correct it.
|
||||
pdfJsWrapper.viewer.currentScaleValue = scaleRef.current
|
||||
setInitialised(true)
|
||||
timePDFFetched = performance.now()
|
||||
if (document.hidden) {
|
||||
|
||||
@@ -86,8 +86,36 @@ export default class PDFJSWrapper {
|
||||
return
|
||||
}
|
||||
|
||||
this.viewer.setDocument(doc)
|
||||
this.linkService.setDocument(doc)
|
||||
// Hold the .pdfViewer element's height steady across the synchronous page-clear
|
||||
// that setDocument() triggers, so the viewer doesn't visually collapse while the
|
||||
// new pages are being initialised. The min-height is released on pagesinit.
|
||||
const viewerEl = this.container.querySelector('.pdfViewer') as HTMLElement
|
||||
const currentHeight = viewerEl.getBoundingClientRect().height
|
||||
if (currentHeight > 0) {
|
||||
viewerEl.style.minHeight = `${currentHeight}px`
|
||||
const clearMinHeight = () => {
|
||||
viewerEl.style.minHeight = ''
|
||||
this.eventBus.off('pagesinit', clearMinHeight)
|
||||
}
|
||||
this.eventBus.on('pagesinit', clearMinHeight)
|
||||
}
|
||||
|
||||
const container = this.container as typeof this.container & {
|
||||
// supported since Chrome 147
|
||||
startViewTransition?: (cb: () => void | Promise<void>) => unknown
|
||||
}
|
||||
if (
|
||||
container.startViewTransition &&
|
||||
typeof container.startViewTransition === 'function'
|
||||
) {
|
||||
container.startViewTransition(() => {
|
||||
this.viewer.setDocument(doc)
|
||||
this.linkService.setDocument(doc)
|
||||
})
|
||||
} else {
|
||||
this.viewer.setDocument(doc)
|
||||
this.linkService.setDocument(doc)
|
||||
}
|
||||
|
||||
return doc
|
||||
} catch (error: any) {
|
||||
@@ -206,14 +234,16 @@ export default class PDFJSWrapper {
|
||||
destArray,
|
||||
})
|
||||
|
||||
// scroll the page left and down by an extra few pixels to account for the pdf.js viewer page border
|
||||
// scrollPageIntoView aligns PDF content to the container top, ignoring the page margin.
|
||||
// For a top-of-document position this leaves scrollTop = marginTop (margin hidden).
|
||||
// Snap back to 0 so the margin is visible, but only when we are in that margin band —
|
||||
// for any real mid-document scrollTop this condition is false and we leave it untouched.
|
||||
const pageIndex = this.viewer.currentPageNumber - 1
|
||||
const pageView = this.viewer.getPageView(pageIndex)
|
||||
const offset = parseFloat(getComputedStyle(pageView.div).borderWidth)
|
||||
this.viewer.container.scrollBy({
|
||||
top: -offset,
|
||||
left: -offset,
|
||||
})
|
||||
const marginTop = parseFloat(getComputedStyle(pageView.div).marginTop)
|
||||
if (this.viewer.container.scrollTop <= marginTop) {
|
||||
this.viewer.container.scrollTop = 0
|
||||
}
|
||||
}
|
||||
|
||||
isVisible() {
|
||||
|
||||
@@ -217,6 +217,7 @@
|
||||
|
||||
.pdf-viewer {
|
||||
isolation: isolate;
|
||||
view-transition-name: pdf-viewer;
|
||||
|
||||
iframe {
|
||||
width: 100%;
|
||||
@@ -255,6 +256,7 @@
|
||||
}
|
||||
|
||||
.page {
|
||||
display: block; // prevent Overleaf's .loading class (display:inline-flex) from affecting PDF.js page state
|
||||
box-sizing: content-box;
|
||||
margin: var(--spacing-05) auto;
|
||||
box-shadow:
|
||||
@@ -262,6 +264,13 @@
|
||||
0 3px 14px 0 #23282f08,
|
||||
0 8px 10px 0 #23282f14;
|
||||
border: none;
|
||||
|
||||
// Overleaf has its own loading state UI; suppress the PDF.js page-level loading
|
||||
// spinner so the loadingIcon/loading classes have no layout or visual effect.
|
||||
/* stylelint-disable-next-line selector-class-pattern */
|
||||
&.loadingIcon::after {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.pdfjs-viewer-inner {
|
||||
@@ -316,6 +325,11 @@
|
||||
}
|
||||
}
|
||||
|
||||
::view-transition-old(pdf-viewer),
|
||||
::view-transition-new(pdf-viewer) {
|
||||
animation-duration: 0.25s;
|
||||
}
|
||||
|
||||
.pdfjs-viewer-controls {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
Reference in New Issue
Block a user