Преглед изворни кода

Reapply "Wrap PDF setDocument in startViewTransition (#33346)" (#33633)

GitOrigin-RevId: 11dc65d8a8195c8cd6e6e2b58905a0f8b7b218f4
Alf Eaton пре 2 месеци
родитељ
комит
34d272afa9

+ 3 - 0
services/web/frontend/js/features/pdf-preview/components/pdf-js-viewer.tsx

@@ -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) {

+ 53 - 8
services/web/frontend/js/features/pdf-preview/util/pdf-js-wrapper.ts

@@ -86,8 +86,49 @@ 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 setDocument = () => {
+        this.viewer.setDocument(doc)
+        this.linkService.setDocument(doc)
+      }
+
+      const container = this.container as typeof this.container & {
+        // supported since Chrome 147
+        startViewTransition?: (cb: () => void | Promise<void>) => {
+          ready: Promise<void>
+        }
+      }
+
+      if (
+        typeof container.startViewTransition === 'function' &&
+        document.visibilityState !== 'hidden'
+      ) {
+        const transition = container.startViewTransition(setDocument)
+
+        transition.ready.catch(err => {
+          // ignore InvalidStateError, it just means the document was hidden
+          if (err?.name !== 'InvalidStateError') {
+            captureException(err, {
+              tags: { handler: 'pdf-preview' },
+            })
+          }
+        })
+      } else {
+        setDocument()
+      }
 
       return doc
     } catch (error: any) {
@@ -206,14 +247,18 @@ 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,
-    })
+    if (pageView) {
+      const marginTop = parseFloat(getComputedStyle(pageView.div).marginTop)
+      if (this.viewer.container.scrollTop <= marginTop) {
+        this.viewer.container.scrollTop = 0
+      }
+    }
   }
 
   isVisible() {

+ 14 - 0
services/web/frontend/stylesheets/pages/editor/pdf.scss

@@ -255,6 +255,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 +263,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 {
@@ -271,6 +279,7 @@
       height: 100%;
       -webkit-font-smoothing: initial;
       -moz-osx-font-smoothing: initial;
+      view-transition-name: pdf-viewer;
 
       /* fix review-panel overflow issue, see: https://github.com/overleaf/internal/issues/6781#issuecomment-1112708638 */
       /* stylelint-disable-next-line selector-class-pattern */
@@ -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;