From 1d98e643ffaf20814f0b45626a78585bc264b1fa Mon Sep 17 00:00:00 2001 From: KDSBrowne Date: Tue, 9 Jan 2024 14:31:08 +0000 Subject: [PATCH 1/4] handle wheel zoom updating display value --- .../ui/components/whiteboard/component.jsx | 138 ++++++++++-------- .../imports/ui/components/whiteboard/hooks.js | 80 ++++++++-- 2 files changed, 151 insertions(+), 67 deletions(-) diff --git a/bigbluebutton-html5/imports/ui/components/whiteboard/component.jsx b/bigbluebutton-html5/imports/ui/components/whiteboard/component.jsx index c0c5627cb3..4b474618b6 100644 --- a/bigbluebutton-html5/imports/ui/components/whiteboard/component.jsx +++ b/bigbluebutton-html5/imports/ui/components/whiteboard/component.jsx @@ -174,6 +174,7 @@ export default Whiteboard = React.memo(function Whiteboard(props) { const isFirstZoomActionRef = useRef(true); const isMouseDownRef = useRef(false); const isMountedRef = useRef(false); + const isWheelZoomRef = useRef(false); const THRESHOLD = 0.1; const lastKnownHeight = React.useRef(presentationAreaHeight); @@ -275,8 +276,33 @@ export default Whiteboard = React.memo(function Whiteboard(props) { } }; + const calculateZoomValue = (localWidth, localHeight, isViewer = false) => { + let calcedZoom; + if (isViewer) { + // Logic originally in calculateViewerZoom + calcedZoom = fitToWidth + ? presentationAreaWidth / localWidth + : Math.min( + presentationAreaWidth / localWidth, + presentationAreaHeight / localHeight + ); + } else { + // Logic originally in calculateZoom + calcedZoom = fitToWidth + ? presentationAreaWidth / localWidth + : Math.min( + presentationAreaWidth / localWidth, + presentationAreaHeight / localHeight + ); + } + + return calcedZoom === 0 || calcedZoom === Infinity + ? HUNDRED_PERCENT + : calcedZoom; + }; + useMouseEvents( - { whiteboardRef, tlEditorRef }, + { whiteboardRef, tlEditorRef, isWheelZoomRef, initialZoomRef }, { isPresenter, hasWBAccess, @@ -288,6 +314,11 @@ export default Whiteboard = React.memo(function Whiteboard(props) { cursorPosition, updateCursorPosition, toggleToolsAnimations, + zoomChanger, + presentationAreaWidth, + presentationAreaHeight, + calculateZoomValue, + currentPresentationPage, } ); @@ -300,7 +331,7 @@ export default Whiteboard = React.memo(function Whiteboard(props) { React.useEffect(() => { zoomValueRef.current = zoomValue; - if (tlEditor && curPageId && currentPresentationPage && isPresenter) { + if (tlEditor && curPageId && currentPresentationPage && isPresenter && isWheelZoomRef.current === false) { const zoomFitSlide = calculateZoomValue( currentPresentationPage.scaledWidth, currentPresentationPage.scaledHeight @@ -324,6 +355,7 @@ export default Whiteboard = React.memo(function Whiteboard(props) { tlEditor?.viewportPageBounds.height, currentPresentationPage.scaledHeight ); + zoomSlide( viewedRegionW, viewedRegionH, @@ -335,7 +367,7 @@ export default Whiteboard = React.memo(function Whiteboard(props) { // Update the previous zoom value ref with the current zoom value prevZoomValueRef.current = zoomValue; - }, [zoomValue, tlEditor, curPageId]); + }, [zoomValue, tlEditor, curPageId, isWheelZoomRef.current]); React.useEffect(() => { // Calculate the absolute difference @@ -419,14 +451,30 @@ export default Whiteboard = React.memo(function Whiteboard(props) { adjustedZoom = baseZoom * (effectiveZoom / HUNDRED_PERCENT); setCamera(adjustedZoom); } - - if (zoomValueRef.current === HUNDRED_PERCENT) { - initialZoomRef.current = adjustedZoom; - } } } }, [presentationAreaHeight, presentationAreaWidth, curPageId]); + React.useEffect(() => { + if ( + presentationAreaHeight > 0 + && presentationAreaWidth > 0 + && tlEditorRef.current + && currentPresentationPage + && currentPresentationPage.scaledWidth > 0 + && currentPresentationPage.scaledHeight > 0 + ) { + // Calculate the base zoom + const baseZoom = calculateZoomValue( + currentPresentationPage.scaledWidth, + currentPresentationPage.scaledHeight + ); + // Set the initial zoom reference + initialZoomRef.current = baseZoom; + } +}, [presentationAreaHeight, presentationAreaWidth, tlEditor, currentPresentationPage]); + + React.useEffect(() => { if (!fitToWidth && isPresenter) { setZoom(HUNDRED_PERCENT); @@ -632,31 +680,6 @@ export default Whiteboard = React.memo(function Whiteboard(props) { } }, [tlEditorRef?.current?.camera, presentationAreaWidth, presentationAreaHeight]); - const calculateZoomValue = (localWidth, localHeight, isViewer = false) => { - let calcedZoom; - if (isViewer) { - // Logic originally in calculateViewerZoom - calcedZoom = fitToWidth - ? presentationAreaWidth / localWidth - : Math.min( - presentationAreaWidth / localWidth, - presentationAreaHeight / localHeight - ); - } else { - // Logic originally in calculateZoom - calcedZoom = fitToWidth - ? presentationAreaWidth / localWidth - : Math.min( - presentationAreaWidth / localWidth, - presentationAreaHeight / localHeight - ); - } - - return calcedZoom === 0 || calcedZoom === Infinity - ? HUNDRED_PERCENT - : calcedZoom; - }; - const handleTldrawMount = (editor) => { setTlEditor(editor); @@ -808,31 +831,32 @@ export default Whiteboard = React.memo(function Whiteboard(props) { next?.id?.includes("camera") && (prev.x !== next.x || prev.y !== next.y); const zoomed = next?.id?.includes("camera") && prev.z !== next.z; - // if (panned && isPresenter) { - // // // limit bounds - // if ( - // editor?.viewportPageBounds?.maxX > - // currentPresentationPage?.scaledWidth - // ) { - // next.x += - // editor.viewportPageBounds.maxX - - // currentPresentationPage?.scaledWidth; - // } - // if ( - // editor?.viewportPageBounds?.maxY > - // currentPresentationPage?.scaledHeight - // ) { - // next.y += - // editor.viewportPageBounds.maxY - - // currentPresentationPage?.scaledHeight; - // } - // if (next.x > 0 || editor.viewportPageBounds.minX < 0) { - // next.x = 0; - // } - // if (next.y > 0 || editor.viewportPageBounds.minY < 0) { - // next.y = 0; - // } - // } + if (panned && isPresenter) { + // // limit bounds + if ( + editor?.viewportPageBounds?.maxX > + currentPresentationPage?.scaledWidth + ) { + next.x += + editor.viewportPageBounds.maxX - + currentPresentationPage?.scaledWidth; + } + if ( + editor?.viewportPageBounds?.maxY > + currentPresentationPage?.scaledHeight + ) { + next.y += + editor.viewportPageBounds.maxY - + currentPresentationPage?.scaledHeight; + } + if (next.x > 0 || editor.viewportPageBounds.minX < 0) { + next.x = 0; + } + if (next.y > 0 || editor.viewportPageBounds.minY < 0) { + next.y = 0; + } + } + return next; }; } diff --git a/bigbluebutton-html5/imports/ui/components/whiteboard/hooks.js b/bigbluebutton-html5/imports/ui/components/whiteboard/hooks.js index b02039b792..fdc2d8dc4b 100644 --- a/bigbluebutton-html5/imports/ui/components/whiteboard/hooks.js +++ b/bigbluebutton-html5/imports/ui/components/whiteboard/hooks.js @@ -1,4 +1,8 @@ import React, { useState, useEffect, useRef } from 'react'; +import SlideCalcUtil, { + HUNDRED_PERCENT, + MAX_PERCENT, +} from "/imports/utils/slideCalcUtils"; const useCursor = (publishCursorUpdate, whiteboardId) => { const [cursorPosition, setCursorPosition] = useState({ x: -1, y: -1 }); @@ -18,7 +22,7 @@ const useCursor = (publishCursorUpdate, whiteboardId) => { return [cursorPosition, updateCursorPosition]; }; -const useMouseEvents = ({ whiteboardRef, tlEditorRef }, { +const useMouseEvents = ({ whiteboardRef, tlEditorRef, isWheelZoomRef, initialZoomRef }, { isPresenter, hasWBAccess, isMouseDownRef, @@ -28,7 +32,12 @@ const useMouseEvents = ({ whiteboardRef, tlEditorRef }, { whiteboardId, cursorPosition, updateCursorPosition, - toggleToolsAnimations + toggleToolsAnimations, + zoomChanger, + presentationAreaWidth, + presentationAreaHeight, + calculateZoomValue, + currentPresentationPage, }) => { const timeoutIdRef = React.useRef(); @@ -80,6 +89,7 @@ const useMouseEvents = ({ whiteboardRef, tlEditorRef }, { }, 150); }; + const handleMouseWheel = (event) => { if (!tlEditorRef.current || !isPresenter) { event.preventDefault(); @@ -87,35 +97,85 @@ const useMouseEvents = ({ whiteboardRef, tlEditorRef }, { return; } + isWheelZoomRef.current = true; + const MAX_ZOOM = 4; - const MIN_ZOOM = .2; - const ZOOM_IN_FACTOR = 0.025; // Finer zoom control - const ZOOM_OUT_FACTOR = 0.025; + const MIN_ZOOM = initialZoomRef.current; + const ZOOM_IN_FACTOR = 0.1; // Adjusted for finer control + const ZOOM_OUT_FACTOR = 0.1; const { x: cx, y: cy, z: cz } = tlEditorRef.current.camera; let zoom = cz; if (event.deltaY < 0) { - // Zoom in zoom = Math.min(cz + ZOOM_IN_FACTOR, MAX_ZOOM); } else { - // Zoom out zoom = Math.max(cz - ZOOM_OUT_FACTOR, MIN_ZOOM); } - const { x, y } = { x: cursorPosition?.x, y: cursorPosition?.y }; + // Base Zoom Calculation using the passed calculateZoomValue function + const baseZoom = calculateZoomValue( + currentPresentationPage.scaledWidth, + currentPresentationPage.scaledHeight + ); + + // Apply aspect ratio adjustments + const displayAspectRatio = presentationAreaWidth / presentationAreaHeight; + const contentAspectRatio = currentPresentationPage.scaledWidth / currentPresentationPage.scaledHeight; + + if (contentAspectRatio > displayAspectRatio) { + zoom *= contentAspectRatio / displayAspectRatio; + } else { + zoom *= displayAspectRatio / contentAspectRatio; + } + + // Adjust zoom based on the base zoom calculation + zoom *= baseZoom; + + const zoomRatio = zoom / initialZoomRef.current; + const backendZoomValue = zoomRatio * 100; + const adjustedBackendZoomValue = Math.min(Math.max(backendZoomValue, 100), 400); + const nextCamera = { - x: cx + (x / zoom - x) - (x / cz - x), - y: cy + (y / zoom - y) - (y / cz - y), + x: cx + (cursorPosition.x / zoom - cursorPosition.x) - (cursorPosition.x / cz - cursorPosition.x), + y: cy + (cursorPosition.y / zoom - cursorPosition.y) - (cursorPosition.y / cz - cursorPosition.y), z: zoom, }; + // Apply the bounds restriction logic after the camera has been updated + const { maxX, maxY, minX, minY } = tlEditorRef.current.viewportPageBounds; + const { scaledWidth, scaledHeight } = currentPresentationPage; + + if (maxX > scaledWidth) { + nextCamera.x += maxX - scaledWidth; + } + if (maxY > scaledHeight) { + nextCamera.y += maxY - scaledHeight; + } + if (nextCamera.x > 0 || minX < 0) { + nextCamera.x = 0; + } + if (nextCamera.y > 0 || minY < 0) { + nextCamera.y = 0; + } + tlEditorRef.current.setCamera(nextCamera, { duration: 300 }); event.preventDefault(); event.stopPropagation(); + + zoomChanger(adjustedBackendZoomValue); + + if (isWheelZoomRef.currentTimeout) { + clearTimeout(isWheelZoomRef.currentTimeout); + } + + isWheelZoomRef.currentTimeout = setTimeout(() => { + isWheelZoomRef.current = false; + }, 300); }; + React.useEffect(() => { if (whiteboardToolbarAutoHide) { toggleToolsAnimations( From 93695e62a3e9d436d7965c2ef1f43679625d8bd7 Mon Sep 17 00:00:00 2001 From: KDSBrowne Date: Tue, 9 Jan 2024 15:02:32 +0000 Subject: [PATCH 2/4] update hook --- .../imports/ui/components/whiteboard/hooks.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/bigbluebutton-html5/imports/ui/components/whiteboard/hooks.js b/bigbluebutton-html5/imports/ui/components/whiteboard/hooks.js index fdc2d8dc4b..a864b398b2 100644 --- a/bigbluebutton-html5/imports/ui/components/whiteboard/hooks.js +++ b/bigbluebutton-html5/imports/ui/components/whiteboard/hooks.js @@ -1,5 +1,5 @@ import React, { useState, useEffect, useRef } from 'react'; -import SlideCalcUtil, { +import { HUNDRED_PERCENT, MAX_PERCENT, } from "/imports/utils/slideCalcUtils"; @@ -133,8 +133,8 @@ const useMouseEvents = ({ whiteboardRef, tlEditorRef, isWheelZoomRef, initialZoo zoom *= baseZoom; const zoomRatio = zoom / initialZoomRef.current; - const backendZoomValue = zoomRatio * 100; - const adjustedBackendZoomValue = Math.min(Math.max(backendZoomValue, 100), 400); + const backendZoomValue = zoomRatio * HUNDRED_PERCENT; + const adjustedBackendZoomValue = Math.min(Math.max(backendZoomValue, HUNDRED_PERCENT), MAX_PERCENT); const nextCamera = { x: cx + (cursorPosition.x / zoom - cursorPosition.x) - (cursorPosition.x / cz - cursorPosition.x), From 271bb22daa7cbefa669de6607adb09b138df62b8 Mon Sep 17 00:00:00 2001 From: KDSBrowne Date: Wed, 31 Jan 2024 18:53:05 +0000 Subject: [PATCH 3/4] fix wheel zoom not working with smaller sizes --- .../ui/components/whiteboard/component.jsx | 26 ++++++-- .../ui/components/whiteboard/container.jsx | 2 + .../imports/ui/components/whiteboard/hooks.js | 62 ++++++------------- 3 files changed, 43 insertions(+), 47 deletions(-) diff --git a/bigbluebutton-html5/imports/ui/components/whiteboard/component.jsx b/bigbluebutton-html5/imports/ui/components/whiteboard/component.jsx index 63c4a17a33..6abba9c7ca 100644 --- a/bigbluebutton-html5/imports/ui/components/whiteboard/component.jsx +++ b/bigbluebutton-html5/imports/ui/components/whiteboard/component.jsx @@ -139,6 +139,8 @@ export default Whiteboard = React.memo(function Whiteboard(props) { isShapeOwner, ShapeStylesContext, hideViewersCursor, + presentationHeight, + presentationWidth, } = props; clearTldrawCache(); @@ -319,11 +321,8 @@ export default Whiteboard = React.memo(function Whiteboard(props) { cursorPosition, updateCursorPosition, toggleToolsAnimations, - zoomChanger, - presentationAreaWidth, - presentationAreaHeight, - calculateZoomValue, currentPresentationPage, + zoomChanger, } ); @@ -374,6 +373,24 @@ export default Whiteboard = React.memo(function Whiteboard(props) { prevZoomValueRef.current = zoomValue; }, [zoomValue, tlEditor, curPageId, isWheelZoomRef.current]); + React.useEffect(() => { + if ( + presentationHeight > 0 + && presentationWidth > 0 + && tlEditorRef.current + && currentPresentationPage + && currentPresentationPage.scaledWidth > 0 + && currentPresentationPage.scaledHeight > 0 + ) { + const baseZoom = calculateZoomValue( + currentPresentationPage.scaledWidth, + currentPresentationPage.scaledHeight + ); + + initialZoomRef.current = baseZoom; + } + }, [presentationHeight, presentationWidth, tlEditorRef, currentPresentationPage]); + React.useEffect(() => { // Calculate the absolute difference const heightDifference = Math.abs( @@ -889,7 +906,6 @@ export default Whiteboard = React.memo(function Whiteboard(props) { next.y = 0; } } - return next; }; } diff --git a/bigbluebutton-html5/imports/ui/components/whiteboard/container.jsx b/bigbluebutton-html5/imports/ui/components/whiteboard/container.jsx index 9f11ce11a9..8d610563e7 100644 --- a/bigbluebutton-html5/imports/ui/components/whiteboard/container.jsx +++ b/bigbluebutton-html5/imports/ui/components/whiteboard/container.jsx @@ -43,6 +43,7 @@ const WhiteboardContainer = (props) => { intl, slidePosition, svgUri, + zoomChanger, } = props; const [annotations, setAnnotations] = useState([]); @@ -247,6 +248,7 @@ const WhiteboardContainer = (props) => { presentationId, hasWBAccess, whiteboardWriters, + zoomChanger, }} {...props} meetingId={Auth.meetingID} diff --git a/bigbluebutton-html5/imports/ui/components/whiteboard/hooks.js b/bigbluebutton-html5/imports/ui/components/whiteboard/hooks.js index 6f32e07264..58e466baff 100644 --- a/bigbluebutton-html5/imports/ui/components/whiteboard/hooks.js +++ b/bigbluebutton-html5/imports/ui/components/whiteboard/hooks.js @@ -33,11 +33,8 @@ const useMouseEvents = ({ whiteboardRef, tlEditorRef, isWheelZoomRef, initialZoo cursorPosition, updateCursorPosition, toggleToolsAnimations, - zoomChanger, - presentationAreaWidth, - presentationAreaHeight, - calculateZoomValue, currentPresentationPage, + zoomChanger, }) => { const timeoutIdRef = React.useRef(); @@ -91,55 +88,39 @@ const useMouseEvents = ({ whiteboardRef, tlEditorRef, isWheelZoomRef, initialZoo const handleMouseWheel = (event) => { + event.preventDefault(); + event.stopPropagation(); if (!tlEditorRef.current || !isPresenter) { - event.preventDefault(); - event.stopPropagation(); return; } isWheelZoomRef.current = true; - const MAX_ZOOM = 4; - const MIN_ZOOM = initialZoomRef.current; - const ZOOM_IN_FACTOR = 0.100; // Adjusted for finer control - const ZOOM_OUT_FACTOR = 0.100; + const MAX_ZOOM_FACTOR = 4; // Represents 400% + const MIN_ZOOM_FACTOR = 1; // Represents 100% + const ZOOM_IN_FACTOR = 0.1; + const ZOOM_OUT_FACTOR = 0.1; const { x: cx, y: cy, z: cz } = tlEditorRef.current.camera; - let zoom = cz; + let currentZoomLevel = tlEditorRef.current.camera.z / initialZoomRef.current; if (event.deltaY < 0) { - zoom = Math.min(cz + ZOOM_IN_FACTOR, MAX_ZOOM); + currentZoomLevel = Math.min(currentZoomLevel + ZOOM_IN_FACTOR, MAX_ZOOM_FACTOR); } else { - zoom = Math.max(cz - ZOOM_OUT_FACTOR, MIN_ZOOM); + currentZoomLevel = Math.max(currentZoomLevel - ZOOM_OUT_FACTOR, MIN_ZOOM_FACTOR); } - // Base Zoom Calculation using the passed calculateZoomValue function - const baseZoom = calculateZoomValue( - currentPresentationPage.scaledWidth, - currentPresentationPage.scaledHeight - ); + // Convert zoom level to a percentage for backend + const zoomPercentage = currentZoomLevel * 100; + zoomChanger(zoomPercentage); - // Apply aspect ratio adjustments - const displayAspectRatio = presentationAreaWidth / presentationAreaHeight; - const contentAspectRatio = currentPresentationPage.scaledWidth / currentPresentationPage.scaledHeight; - - if (contentAspectRatio > displayAspectRatio) { - zoom *= contentAspectRatio / displayAspectRatio; - } else { - zoom *= displayAspectRatio / contentAspectRatio; - } - - // Adjust zoom based on the base zoom calculation - zoom *= baseZoom; - - const zoomRatio = zoom / initialZoomRef.current; - const backendZoomValue = zoomRatio * HUNDRED_PERCENT; - const adjustedBackendZoomValue = Math.min(Math.max(backendZoomValue, HUNDRED_PERCENT), MAX_PERCENT); + // Calculate the new camera zoom factor + const newCameraZoomFactor = currentZoomLevel * initialZoomRef.current; const nextCamera = { - x: cx + (cursorPosition.x / zoom - cursorPosition.x) - (cursorPosition.x / cz - cursorPosition.x), - y: cy + (cursorPosition.y / zoom - cursorPosition.y) - (cursorPosition.y / cz - cursorPosition.y), - z: zoom, + x: cx + (cursorPosition.x / newCameraZoomFactor - cursorPosition.x) - (cursorPosition.x / cz - cursorPosition.x), + y: cy + (cursorPosition.y / newCameraZoomFactor - cursorPosition.y) - (cursorPosition.y / cz - cursorPosition.y), + z: newCameraZoomFactor, }; // Apply the bounds restriction logic after the camera has been updated @@ -161,11 +142,6 @@ const useMouseEvents = ({ whiteboardRef, tlEditorRef, isWheelZoomRef, initialZoo tlEditorRef.current.setCamera(nextCamera, { duration: 300 }); - event.preventDefault(); - event.stopPropagation(); - - zoomChanger(adjustedBackendZoomValue); - if (isWheelZoomRef.currentTimeout) { clearTimeout(isWheelZoomRef.currentTimeout); } @@ -217,6 +193,8 @@ const useMouseEvents = ({ whiteboardRef, tlEditorRef, isWheelZoomRef, initialZoo }, [whiteboardRef, tlEditorRef, handleMouseDown, handleMouseUp, handleMouseEnter, handleMouseLeave, handleMouseWheel]); }; + + export { useMouseEvents, useCursor, From 89ff1811031d75ed277c9ff87acc359c3329f1ca Mon Sep 17 00:00:00 2001 From: KDSBrowne Date: Fri, 2 Feb 2024 00:36:40 +0000 Subject: [PATCH 4/4] preserve relative zoom focal point when value updates --- .../ui/components/whiteboard/component.jsx | 70 ++++++++++--------- 1 file changed, 36 insertions(+), 34 deletions(-) diff --git a/bigbluebutton-html5/imports/ui/components/whiteboard/component.jsx b/bigbluebutton-html5/imports/ui/components/whiteboard/component.jsx index 6abba9c7ca..04e8c50172 100644 --- a/bigbluebutton-html5/imports/ui/components/whiteboard/component.jsx +++ b/bigbluebutton-html5/imports/ui/components/whiteboard/component.jsx @@ -331,7 +331,6 @@ export default Whiteboard = React.memo(function Whiteboard(props) { tlEditorRef.current = tlEditor; }, [tlEditor]); - // presenter effect to handle zoomSlide React.useEffect(() => { zoomValueRef.current = zoomValue; @@ -342,29 +341,52 @@ export default Whiteboard = React.memo(function Whiteboard(props) { ); const zoomCamera = (zoomFitSlide * zoomValue) / HUNDRED_PERCENT; - // Compare the current zoom value with the previous one - if (zoomValue !== prevZoomValueRef.current) { - tlEditor?.setCamera( - { - z: zoomCamera, - }, - false - ); + // Assuming centerX and centerY represent the center of the current view + const centerX = tlEditor.camera.x + (tlEditor.viewportPageBounds.width / 2) / tlEditor.camera.z; + const centerY = tlEditor.camera.y + (tlEditor.viewportPageBounds.height / 2) / tlEditor.camera.z; + // Calculate the new camera position to keep the center in focus after zoom + const nextCamera = { + x: centerX + (centerX / zoomCamera - centerX) - (centerX / tlEditor.camera.z - centerX), + y: centerY + (centerY / zoomCamera - centerY) - (centerY / tlEditor.camera.z - centerY), + z: zoomCamera, + }; + + // Apply bounds restriction logic + const { maxX, maxY, minX, minY } = tlEditor.viewportPageBounds; + const { scaledWidth, scaledHeight } = currentPresentationPage; + + if (maxX > scaledWidth) { + nextCamera.x += maxX - scaledWidth; + } + if (maxY > scaledHeight) { + nextCamera.y += maxY - scaledHeight; + } + if (nextCamera.x > 0 || minX < 0) { + nextCamera.x = 0; + } + if (nextCamera.y > 0 || minY < 0) { + nextCamera.y = 0; + } + + if (zoomValue !== prevZoomValueRef.current) { + tlEditor.setCamera(nextCamera, false); + + // Recalculate viewed region width and height if necessary for zoomSlide call let viewedRegionW = SlideCalcUtil.calcViewedRegionWidth( - tlEditor?.viewportPageBounds.width, + tlEditor.viewportPageBounds.width, currentPresentationPage.scaledWidth ); let viewedRegionH = SlideCalcUtil.calcViewedRegionHeight( - tlEditor?.viewportPageBounds.height, + tlEditor.viewportPageBounds.height, currentPresentationPage.scaledHeight ); zoomSlide( viewedRegionW, viewedRegionH, - tlEditor.camera.x, - tlEditor.camera.y, + nextCamera.x, + nextCamera.y, ); } } @@ -389,7 +411,7 @@ export default Whiteboard = React.memo(function Whiteboard(props) { initialZoomRef.current = baseZoom; } - }, [presentationHeight, presentationWidth, tlEditorRef, currentPresentationPage]); + }, [presentationAreaHeight, presentationHeight, presentationAreaWidth, presentationWidth, tlEditorRef, currentPresentationPage]); React.useEffect(() => { // Calculate the absolute difference @@ -478,26 +500,6 @@ export default Whiteboard = React.memo(function Whiteboard(props) { } }, [presentationAreaHeight, presentationAreaWidth, curPageId]); - React.useEffect(() => { - if ( - presentationAreaHeight > 0 - && presentationAreaWidth > 0 - && tlEditorRef.current - && currentPresentationPage - && currentPresentationPage.scaledWidth > 0 - && currentPresentationPage.scaledHeight > 0 - ) { - // Calculate the base zoom - const baseZoom = calculateZoomValue( - currentPresentationPage.scaledWidth, - currentPresentationPage.scaledHeight - ); - // Set the initial zoom reference - initialZoomRef.current = baseZoom; - } -}, [presentationAreaHeight, presentationAreaWidth, tlEditor, currentPresentationPage]); - - React.useEffect(() => { if (!fitToWidth && isPresenter) { setZoom(HUNDRED_PERCENT);