@@ -35,8 +35,8 @@ pdfjsLib.GlobalWorkerOptions.workerSrc = new URL(
3535const ZOOM_STEPS = [ 0.5 , 0.75 , 1 , 1.25 , 1.5 , 2 ] ;
3636const MIN_ZOOM = 0.25 ;
3737const MAX_ZOOM = 4 ;
38- const PINCH_SENSITIVITY = 0.0025 ;
39- const PINCH_COMMIT_DELAY = 150 ;
38+ const WHEEL_ZOOM_SENSITIVITY = 0.0025 ;
39+ const WHEEL_ZOOM_COMMIT_DELAY = 150 ;
4040const CONTINUOUS_PAGE_GAP = 20 ;
4141const CONTINUOUS_OVERSCAN_PX = 1200 ;
4242const CONTINUOUS_MAX_RENDERED_PAGES = 15 ;
@@ -260,9 +260,7 @@ function PdfViewer({ tab }: { tab: PdfTab }) {
260260 const pageRefs = useRef < Record < number , HTMLDivElement | null > > ( { } ) ;
261261 const previousViewModeRef = useRef ( tab . viewMode ) ;
262262 const pendingProgrammaticPageRef = useRef < number | null > ( null ) ;
263- const pinchZoomRef = useRef ( tab . zoom ) ;
264- const pinchTimerRef = useRef ( 0 ) ;
265- const pendingWheelZoomRef = useRef < number | null > ( null ) ;
263+ const wheelZoomTimerRef = useRef ( 0 ) ;
266264 const wheelZoomModifierRef = useWheelZoomModifier ( ) ;
267265 const [ contextMenu , setContextMenu ] = useState < ContextMenuState < {
268266 pageNumber : number ;
@@ -277,6 +275,7 @@ function PdfViewer({ tab }: { tab: PdfTab }) {
277275 null ,
278276 ) ;
279277 const [ retryCount , setRetryCount ] = useState ( 0 ) ;
278+ const [ liveZoom , setLiveZoom ] = useState < number | null > ( null ) ;
280279 const [ scrollTop , setScrollTop ] = useState ( 0 ) ;
281280 const [ scrollContainer , setScrollContainer ] =
282281 useState < HTMLDivElement | null > ( null ) ;
@@ -303,9 +302,11 @@ function PdfViewer({ tab }: { tab: PdfTab }) {
303302 const loading = ! error && ! activePdf ;
304303 const pdf = activePdf ?. pdf ?? null ;
305304 const numPages = activePdf ?. numPages ?? 0 ;
305+ const effectiveZoom = liveZoom ?? tab . zoom ;
306+ const effectiveZoomRef = useRef ( effectiveZoom ) ;
306307 const continuousLayouts = useMemo (
307- ( ) => ( pageMetrics ? buildPageLayouts ( pageMetrics , tab . zoom ) : [ ] ) ,
308- [ pageMetrics , tab . zoom ] ,
308+ ( ) => ( pageMetrics ? buildPageLayouts ( pageMetrics , effectiveZoom ) : [ ] ) ,
309+ [ effectiveZoom , pageMetrics ] ,
309310 ) ;
310311 const effectiveViewportHeight = Math . max (
311312 viewportHeight ,
@@ -396,7 +397,29 @@ function PdfViewer({ tab }: { tab: PdfTab }) {
396397
397398 useEffect ( ( ) => {
398399 pageRefs . current = { } ;
399- } , [ tab . path , tab . viewMode , tab . zoom , retryCount ] ) ;
400+ } , [ effectiveZoom , retryCount , tab . path , tab . viewMode ] ) ;
401+
402+ useEffect ( ( ) => {
403+ effectiveZoomRef . current = effectiveZoom ;
404+ } , [ effectiveZoom ] ) ;
405+
406+ useEffect ( ( ) => {
407+ if ( liveZoom === null ) return ;
408+ if ( Math . abs ( tab . zoom - liveZoom ) > 0.0001 ) return ;
409+ setLiveZoom ( null ) ;
410+ } , [ liveZoom , tab . zoom ] ) ;
411+
412+ useEffect ( ( ) => {
413+ return ( ) => {
414+ window . clearTimeout ( wheelZoomTimerRef . current ) ;
415+ } ;
416+ } , [ ] ) ;
417+
418+ useEffect ( ( ) => {
419+ window . clearTimeout ( wheelZoomTimerRef . current ) ;
420+ setLiveZoom ( null ) ;
421+ effectiveZoomRef . current = tab . zoom ;
422+ } , [ tab . id , tab . path ] ) ;
400423
401424 useEffect ( ( ) => {
402425 queueMicrotask ( ( ) => setPageMetrics ( null ) ) ;
@@ -614,12 +637,16 @@ function PdfViewer({ tab }: { tab: PdfTab }) {
614637 } , [ numPages , scrollToPage , tab . id , tab . page , tab . viewMode , updatePdfPage ] ) ;
615638
616639 const zoomIn = useCallback ( ( ) => {
617- updatePdfZoom ( tab . id , clampZoom ( tab . zoom , "in" ) ) ;
618- } , [ tab . id , tab . zoom , updatePdfZoom ] ) ;
640+ window . clearTimeout ( wheelZoomTimerRef . current ) ;
641+ setLiveZoom ( null ) ;
642+ updatePdfZoom ( tab . id , clampZoom ( effectiveZoom , "in" ) ) ;
643+ } , [ effectiveZoom , tab . id , updatePdfZoom ] ) ;
619644
620645 const zoomOut = useCallback ( ( ) => {
621- updatePdfZoom ( tab . id , clampZoom ( tab . zoom , "out" ) ) ;
622- } , [ tab . id , tab . zoom , updatePdfZoom ] ) ;
646+ window . clearTimeout ( wheelZoomTimerRef . current ) ;
647+ setLiveZoom ( null ) ;
648+ updatePdfZoom ( tab . id , clampZoom ( effectiveZoom , "out" ) ) ;
649+ } , [ effectiveZoom , tab . id , updatePdfZoom ] ) ;
623650
624651 const toggleViewMode = useCallback ( ( ) => {
625652 const nextViewMode =
@@ -676,24 +703,6 @@ function PdfViewer({ tab }: { tab: PdfTab }) {
676703 } ) ;
677704 } , [ ] ) ;
678705
679- useEffect ( ( ) => {
680- pinchZoomRef . current = tab . zoom ;
681- if ( pendingWheelZoomRef . current === null ) {
682- return ;
683- }
684-
685- if ( Math . abs ( tab . zoom - pendingWheelZoomRef . current ) > 0.0001 ) {
686- return ;
687- }
688-
689- const content = contentRef . current ;
690- if ( content ) {
691- content . style . transform = "" ;
692- content . style . transformOrigin = "" ;
693- }
694- pendingWheelZoomRef . current = null ;
695- } , [ tab . zoom ] ) ;
696-
697706 useEffect ( ( ) => {
698707 if ( ! scrollContainer ) return ;
699708
@@ -702,20 +711,19 @@ function PdfViewer({ tab }: { tab: PdfTab }) {
702711 if ( ! isWheelZoomGesture ( event , wheelZoomModifierRef ) ) return ;
703712 event . preventDefault ( ) ;
704713
705- const prev = pinchZoomRef . current ;
714+ const prev = effectiveZoomRef . current ;
706715 const next = Math . min (
707716 MAX_ZOOM ,
708717 Math . max (
709718 MIN_ZOOM ,
710- prev * ( 1 - event . deltaY * PINCH_SENSITIVITY ) ,
719+ prev * ( 1 - event . deltaY * WHEEL_ZOOM_SENSITIVITY ) ,
711720 ) ,
712721 ) ;
713- pinchZoomRef . current = next ;
722+ if ( Math . abs ( next - prev ) < 0.0001 ) return ;
714723
715724 const containerRect = scrollContainer . getBoundingClientRect ( ) ;
716725 const pointerOffsetX = event . clientX - containerRect . left ;
717726 const pointerOffsetY = event . clientY - containerRect . top ;
718- const visualScaleRatio = next / tab . zoom ;
719727 const scrollScaleRatio = next / prev ;
720728 const nextScrollLeft = clampScrollOffset (
721729 ( scrollContainer . scrollLeft + pointerOffsetX ) *
@@ -728,42 +736,26 @@ function PdfViewer({ tab }: { tab: PdfTab }) {
728736 pointerOffsetY ,
729737 ) ;
730738
731- const content = contentRef . current ;
732- if ( content ) {
733- content . style . transformOrigin = "0 0" ;
734- content . style . transform = `scale(${ visualScaleRatio } )` ;
735- }
736-
739+ setLiveZoom ( next ) ;
740+ effectiveZoomRef . current = next ;
737741 scrollContainer . scrollLeft = nextScrollLeft ;
738742 scrollContainer . scrollTop = nextScrollTop ;
743+ setScrollTop ( nextScrollTop ) ;
739744
740- window . clearTimeout ( pinchTimerRef . current ) ;
741- pendingWheelZoomRef . current = next ;
742- pinchTimerRef . current = window . setTimeout ( ( ) => {
745+ window . clearTimeout ( wheelZoomTimerRef . current ) ;
746+ wheelZoomTimerRef . current = window . setTimeout ( ( ) => {
743747 updatePdfZoom ( tab . id , persistWheelZoom ( next ) ) ;
744- } , PINCH_COMMIT_DELAY ) ;
748+ } , WHEEL_ZOOM_COMMIT_DELAY ) ;
745749 }
746750
747751 scrollContainer . addEventListener ( "wheel" , handleWheel , {
748752 passive : false ,
749753 } ) ;
750- const capturedContent = contentRef . current ;
751754 return ( ) => {
752755 scrollContainer . removeEventListener ( "wheel" , handleWheel ) ;
753- window . clearTimeout ( pinchTimerRef . current ) ;
754- pendingWheelZoomRef . current = null ;
755- if ( capturedContent ) {
756- capturedContent . style . transform = "" ;
757- capturedContent . style . transformOrigin = "" ;
758- }
756+ window . clearTimeout ( wheelZoomTimerRef . current ) ;
759757 } ;
760- } , [
761- scrollContainer ,
762- tab . id ,
763- tab . zoom ,
764- updatePdfZoom ,
765- wheelZoomModifierRef ,
766- ] ) ;
758+ } , [ scrollContainer , tab . id , updatePdfZoom , wheelZoomModifierRef ] ) ;
767759
768760 useEffect ( ( ) => {
769761 function handleKeyDown ( event : KeyboardEvent ) {
@@ -940,7 +932,7 @@ function PdfViewer({ tab }: { tab: PdfTab }) {
940932
941933 < ToolbarButton
942934 onClick = { zoomOut }
943- disabled = { tab . zoom <= ZOOM_STEPS [ 0 ] }
935+ disabled = { effectiveZoom <= ZOOM_STEPS [ 0 ] }
944936 title = "Zoom out"
945937 >
946938 < MinusIcon />
@@ -952,11 +944,13 @@ function PdfViewer({ tab }: { tab: PdfTab }) {
952944 fontVariantNumeric : "tabular-nums" ,
953945 } }
954946 >
955- { formatZoomPercentage ( tab . zoom ) }
947+ { formatZoomPercentage ( effectiveZoom ) }
956948 </ span >
957949 < ToolbarButton
958950 onClick = { zoomIn }
959- disabled = { tab . zoom >= ZOOM_STEPS [ ZOOM_STEPS . length - 1 ] }
951+ disabled = {
952+ effectiveZoom >= ZOOM_STEPS [ ZOOM_STEPS . length - 1 ]
953+ }
960954 title = "Zoom in"
961955 >
962956 < PlusIcon />
@@ -1021,15 +1015,14 @@ function PdfViewer({ tab }: { tab: PdfTab }) {
10211015 padding : 24 ,
10221016 background :
10231017 "color-mix(in srgb, var(--bg-primary) 92%, #000)" ,
1024- touchAction : "none " ,
1018+ touchAction : "pan-x pan-y pinch-zoom " ,
10251019 } }
10261020 >
10271021 { tab . viewMode === "continuous" ? (
10281022 < div
10291023 ref = { contentRef }
10301024 className = "w-full"
10311025 style = { {
1032- willChange : "transform" ,
10331026 filter : activeFilter . css ,
10341027 position :
10351028 continuousLayouts . length > 0
@@ -1051,10 +1044,10 @@ function PdfViewer({ tab }: { tab: PdfTab }) {
10511044 ) : (
10521045 visibleContinuousLayouts . map ( ( layout ) => (
10531046 < PdfPageCanvas
1054- key = { `${ tab . path } :${ retryCount } :${ layout . pageNumber } :${ tab . zoom } ` }
1047+ key = { `${ tab . path } :${ retryCount } :${ layout . pageNumber } :${ effectiveZoom } ` }
10551048 pdf = { pdf }
10561049 pageNumber = { layout . pageNumber }
1057- zoom = { tab . zoom }
1050+ zoom = { effectiveZoom }
10581051 onRenderError = { setPdfError }
10591052 onContextMenu = { handlePdfContextMenu }
10601053 registerElement = { registerPageElement }
@@ -1072,15 +1065,14 @@ function PdfViewer({ tab }: { tab: PdfTab }) {
10721065 ref = { contentRef }
10731066 className = "w-full flex justify-center"
10741067 style = { {
1075- willChange : "transform" ,
10761068 filter : activeFilter . css ,
10771069 } }
10781070 >
10791071 < PdfPageCanvas
1080- key = { `${ tab . path } :${ retryCount } :${ tab . page } :${ tab . zoom } ` }
1072+ key = { `${ tab . path } :${ retryCount } :${ tab . page } :${ effectiveZoom } ` }
10811073 pdf = { pdf }
10821074 pageNumber = { tab . page }
1083- zoom = { tab . zoom }
1075+ zoom = { effectiveZoom }
10841076 onRenderError = { setPdfError }
10851077 onContextMenu = { handlePdfContextMenu }
10861078 />
@@ -1242,7 +1234,6 @@ function PdfPageCanvas({
12421234 style = { {
12431235 boxShadow : "0 2px 16px rgba(0,0,0,0.15)" ,
12441236 background : "#fff" ,
1245- touchAction : "none" ,
12461237 } }
12471238 />
12481239 < div
0 commit comments