diff --git a/services/web/frontend/js/features/pdf-preview/util/pdf-js-wrapper.ts b/services/web/frontend/js/features/pdf-preview/util/pdf-js-wrapper.ts index 9d7b154d63..a50414f7b2 100644 --- a/services/web/frontend/js/features/pdf-preview/util/pdf-js-wrapper.ts +++ b/services/web/frontend/js/features/pdf-preview/util/pdf-js-wrapper.ts @@ -118,9 +118,25 @@ export default class PDFJSWrapper { }) const container = this.container as typeof this.container & { - // supported since Chrome 126 + // element-level View Transitions (Level 2), Chrome 126+ startViewTransition?: (cb: () => void | Promise) => { ready: Promise + finished: Promise + } + } + const doc_ = document as typeof document & { + // document-level View Transitions (Level 1), Chrome 111+ + startViewTransition?: (cb: () => void | Promise) => { + ready: Promise + finished: Promise + } + } + + // Shared error handler for transition.ready rejections. + const onTransitionError = (err: any) => { + // InvalidStateError just means the document was hidden during the transition. + if (err?.name !== 'InvalidStateError') { + captureException(err, { tags: { handler: 'pdf-preview' } }) } } @@ -128,27 +144,33 @@ export default class PDFJSWrapper { typeof container.startViewTransition === 'function' && document.visibilityState !== 'hidden' ) { - // Chrome 126+: element-level View Transition API. - // The async callback holds the "before" snapshot until pagerendered fires, - // so the crossfade goes old→new rather than old→blank. + // Chrome 126+: element-level View Transition, scoped to this container. + // Async callback holds the "before" snapshot until pagerendered fires. const transition = container.startViewTransition(async () => { setDocument() await firstPageRendered }) - - transition.ready.catch(err => { - // ignore InvalidStateError, it just means the document was hidden - if (err?.name !== 'InvalidStateError') { - captureException(err, { - tags: { handler: 'pdf-preview' }, - }) - } + transition.ready.catch(onTransitionError) + } else if ( + typeof doc_.startViewTransition === 'function' && + document.visibilityState !== 'hidden' + ) { + // Chrome 111+ / Edge 111+: document-level View Transition. + // Give the PDF container a unique view-transition-name so only it + // crossfades; the rest of the page stays static. + const name = 'ol-pdf-viewer' + this.container.style.viewTransitionName = name + const transition = doc_.startViewTransition(async () => { + setDocument() + await firstPageRendered }) + transition.finished.finally(() => { + this.container.style.viewTransitionName = '' + }) + transition.ready.catch(onTransitionError) } else { - // All other browsers: snapshot the currently-rendered canvases into a - // temporary overlay so setDocument()'s synchronous canvas-clear doesn't - // produce a white flash. The overlay is removed once the first page of - // the new document has been painted. + // Firefox / Safari / very old Chromium: canvas snapshot overlay keeps + // the old content visible while setDocument() clears and repaints. const snap = this._snapshotCanvases() setDocument() if (snap) {