From 1f66a136ec4cc7701e20f95967eaa9ba3a345c20 Mon Sep 17 00:00:00 2001 From: Ovgodd Date: Wed, 9 Sep 2026 10:50:39 +0200 Subject: [PATCH] =?UTF-8?q?fixup!=20=E2=99=BB=EF=B8=8F(frontend)=20refacto?= =?UTF-8?q?r=20screen=20share=20zoom=20pan=20with=20useMove=20and=20impera?= =?UTF-8?q?tive=20DOM=20updates?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../rooms/livekit/hooks/useScreenShareZoom.ts | 83 ++++++++++++------- 1 file changed, 55 insertions(+), 28 deletions(-) diff --git a/src/frontend/src/features/rooms/livekit/hooks/useScreenShareZoom.ts b/src/frontend/src/features/rooms/livekit/hooks/useScreenShareZoom.ts index 4b65ea83..1ae4ddac 100644 --- a/src/frontend/src/features/rooms/livekit/hooks/useScreenShareZoom.ts +++ b/src/frontend/src/features/rooms/livekit/hooks/useScreenShareZoom.ts @@ -29,8 +29,8 @@ import { * when the toolbar UI needs to update (zoom level change, drag end). * * Drag/touch panning is handled by react-aria's useMove (moveProps). - * Ctrl/Cmd + wheel zoom is a native listener (must be non-passive to - * preventDefault and block browser page zoom). + * The wheel listener (non-passive) zooms on Ctrl/Cmd+scroll and pans on a + * two-finger trackpad scroll once zoomed. * Arrow key panning and +/-/0 zoom are on a keydown listener attached to the * tile container (which has tabIndex=0 and focus). */ @@ -127,39 +127,66 @@ export const useScreenShareZoom = () => { const resetZoom = useCallback(() => setZoom(MIN_ZOOM), [setZoom]) // Must be attached with { passive: false } so preventDefault() blocks - // the browser's native Ctrl+scroll page zoom. + // the browser's native Ctrl+scroll page zoom. Trackpad pinch arrives + // here as a wheel event with ctrl/cmd already set. const handleWheel = useCallback( (e: WheelEvent) => { - if (!e.ctrlKey && !e.metaKey) return + if (e.ctrlKey || e.metaKey) { + e.preventDefault() + e.stopPropagation() + + const target = e.currentTarget as HTMLElement + const prev = zoomRef.current + const delta = -e.deltaY * WHEEL_ZOOM_SPEED + const next = clampZoom(prev + delta) + + if (next <= MIN_ZOOM) { + zoomRef.current = MIN_ZOOM + panRef.current = { x: 0, y: 0 } + } else { + const { cursorXPercent, cursorYPercent } = + getCursorPercentsFromWheelEvent(e, target) + zoomRef.current = next + panRef.current = getWheelPanOffset({ + pan: panRef.current, + prevZoom: prev, + nextZoom: next, + cursorXPercent, + cursorYPercent, + ratio: readPictureRatio(), + }) + } + + applyTransform() + applyCursor() + flush() + return + } + + // Two-finger trackpad scroll: pan only once zoomed, otherwise leave + // the event alone so the page can still scroll. + if (zoomRef.current <= MIN_ZOOM) return + + const el = surfaceElRef.current + if (!el) return e.preventDefault() e.stopPropagation() - const target = e.currentTarget as HTMLElement - const prev = zoomRef.current - const delta = -e.deltaY * WHEEL_ZOOM_SPEED - const next = clampZoom(prev + delta) - - if (next <= MIN_ZOOM) { - zoomRef.current = MIN_ZOOM - panRef.current = { x: 0, y: 0 } - } else { - const { cursorXPercent, cursorYPercent } = - getCursorPercentsFromWheelEvent(e, target) - zoomRef.current = next - panRef.current = getWheelPanOffset({ - pan: panRef.current, - prevZoom: prev, - nextZoom: next, - cursorXPercent, - cursorYPercent, - ratio: readPictureRatio(), - }) - } - + const { deltaXPercent, deltaYPercent } = getPanDeltaPercentsFromMove( + -e.deltaX, + -e.deltaY, + el + ) + panRef.current = clampPan( + { + x: panRef.current.x + deltaXPercent, + y: panRef.current.y + deltaYPercent, + }, + zoomRef.current, + readPictureRatio() + ) applyTransform() - applyCursor() - flush() }, [applyTransform, applyCursor, flush, readPictureRatio] )