Merge pull request #19402 from KDSBrowne/v3.wheelZoomDisplayVal
Fix: Update Presentation Toolbar Zoom Percentage on Wheel Zoom
This commit is contained in:
commit
08d2206e27
@ -137,6 +137,8 @@ export default Whiteboard = React.memo(function Whiteboard(props) {
|
|||||||
isShapeOwner,
|
isShapeOwner,
|
||||||
ShapeStylesContext,
|
ShapeStylesContext,
|
||||||
hideViewersCursor,
|
hideViewersCursor,
|
||||||
|
presentationHeight,
|
||||||
|
presentationWidth,
|
||||||
} = props;
|
} = props;
|
||||||
|
|
||||||
clearTldrawCache();
|
clearTldrawCache();
|
||||||
@ -163,6 +165,7 @@ export default Whiteboard = React.memo(function Whiteboard(props) {
|
|||||||
const isFirstZoomActionRef = useRef(true);
|
const isFirstZoomActionRef = useRef(true);
|
||||||
const isMouseDownRef = useRef(false);
|
const isMouseDownRef = useRef(false);
|
||||||
const isMountedRef = useRef(false);
|
const isMountedRef = useRef(false);
|
||||||
|
const isWheelZoomRef = useRef(false);
|
||||||
|
|
||||||
const THRESHOLD = 0.1;
|
const THRESHOLD = 0.1;
|
||||||
const lastKnownHeight = React.useRef(presentationAreaHeight);
|
const lastKnownHeight = React.useRef(presentationAreaHeight);
|
||||||
@ -278,8 +281,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(
|
useMouseEvents(
|
||||||
{ whiteboardRef, tlEditorRef },
|
{ whiteboardRef, tlEditorRef, isWheelZoomRef, initialZoomRef },
|
||||||
{
|
{
|
||||||
isPresenter,
|
isPresenter,
|
||||||
hasWBAccess,
|
hasWBAccess,
|
||||||
@ -291,6 +319,8 @@ export default Whiteboard = React.memo(function Whiteboard(props) {
|
|||||||
cursorPosition,
|
cursorPosition,
|
||||||
updateCursorPosition,
|
updateCursorPosition,
|
||||||
toggleToolsAnimations,
|
toggleToolsAnimations,
|
||||||
|
currentPresentationPage,
|
||||||
|
zoomChanger,
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -299,46 +329,87 @@ export default Whiteboard = React.memo(function Whiteboard(props) {
|
|||||||
tlEditorRef.current = tlEditor;
|
tlEditorRef.current = tlEditor;
|
||||||
}, [tlEditor]);
|
}, [tlEditor]);
|
||||||
|
|
||||||
// presenter effect to handle zoomSlide
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
zoomValueRef.current = zoomValue;
|
zoomValueRef.current = zoomValue;
|
||||||
|
|
||||||
if (tlEditor && curPageId && currentPresentationPage && isPresenter) {
|
if (tlEditor && curPageId && currentPresentationPage && isPresenter && isWheelZoomRef.current === false) {
|
||||||
const zoomFitSlide = calculateZoomValue(
|
const zoomFitSlide = calculateZoomValue(
|
||||||
currentPresentationPage.scaledWidth,
|
currentPresentationPage.scaledWidth,
|
||||||
currentPresentationPage.scaledHeight
|
currentPresentationPage.scaledHeight
|
||||||
);
|
);
|
||||||
const zoomCamera = (zoomFitSlide * zoomValue) / HUNDRED_PERCENT;
|
const zoomCamera = (zoomFitSlide * zoomValue) / HUNDRED_PERCENT;
|
||||||
|
|
||||||
// Compare the current zoom value with the previous one
|
// Assuming centerX and centerY represent the center of the current view
|
||||||
if (zoomValue !== prevZoomValueRef.current) {
|
const centerX = tlEditor.camera.x + (tlEditor.viewportPageBounds.width / 2) / tlEditor.camera.z;
|
||||||
tlEditor?.setCamera(
|
const centerY = tlEditor.camera.y + (tlEditor.viewportPageBounds.height / 2) / tlEditor.camera.z;
|
||||||
{
|
|
||||||
z: zoomCamera,
|
|
||||||
},
|
|
||||||
false
|
|
||||||
);
|
|
||||||
|
|
||||||
|
// 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(
|
let viewedRegionW = SlideCalcUtil.calcViewedRegionWidth(
|
||||||
tlEditor?.viewportPageBounds.width,
|
tlEditor.viewportPageBounds.width,
|
||||||
currentPresentationPage.scaledWidth
|
currentPresentationPage.scaledWidth
|
||||||
);
|
);
|
||||||
let viewedRegionH = SlideCalcUtil.calcViewedRegionHeight(
|
let viewedRegionH = SlideCalcUtil.calcViewedRegionHeight(
|
||||||
tlEditor?.viewportPageBounds.height,
|
tlEditor.viewportPageBounds.height,
|
||||||
currentPresentationPage.scaledHeight
|
currentPresentationPage.scaledHeight
|
||||||
);
|
);
|
||||||
|
|
||||||
zoomSlide(
|
zoomSlide(
|
||||||
viewedRegionW,
|
viewedRegionW,
|
||||||
viewedRegionH,
|
viewedRegionH,
|
||||||
tlEditor.camera.x,
|
nextCamera.x,
|
||||||
tlEditor.camera.y,
|
nextCamera.y,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Update the previous zoom value ref with the current zoom value
|
// Update the previous zoom value ref with the current zoom value
|
||||||
prevZoomValueRef.current = zoomValue;
|
prevZoomValueRef.current = zoomValue;
|
||||||
}, [zoomValue, tlEditor, curPageId]);
|
}, [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;
|
||||||
|
}
|
||||||
|
}, [presentationAreaHeight, presentationHeight, presentationAreaWidth, presentationWidth, tlEditorRef, currentPresentationPage]);
|
||||||
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
// Calculate the absolute difference
|
// Calculate the absolute difference
|
||||||
@ -423,10 +494,6 @@ export default Whiteboard = React.memo(function Whiteboard(props) {
|
|||||||
adjustedZoom = baseZoom * (effectiveZoom / HUNDRED_PERCENT);
|
adjustedZoom = baseZoom * (effectiveZoom / HUNDRED_PERCENT);
|
||||||
setCamera(adjustedZoom);
|
setCamera(adjustedZoom);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (zoomValueRef.current === HUNDRED_PERCENT) {
|
|
||||||
initialZoomRef.current = adjustedZoom;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, [presentationAreaHeight, presentationAreaWidth, curPageId]);
|
}, [presentationAreaHeight, presentationAreaWidth, curPageId]);
|
||||||
@ -641,31 +708,6 @@ export default Whiteboard = React.memo(function Whiteboard(props) {
|
|||||||
presentationAreaHeight,
|
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) => {
|
const handleTldrawMount = (editor) => {
|
||||||
setTlEditor(editor);
|
setTlEditor(editor);
|
||||||
|
|
||||||
@ -839,31 +881,31 @@ export default Whiteboard = React.memo(function Whiteboard(props) {
|
|||||||
next?.id?.includes("camera") &&
|
next?.id?.includes("camera") &&
|
||||||
(prev.x !== next.x || prev.y !== next.y);
|
(prev.x !== next.x || prev.y !== next.y);
|
||||||
const zoomed = next?.id?.includes("camera") && prev.z !== next.z;
|
const zoomed = next?.id?.includes("camera") && prev.z !== next.z;
|
||||||
// if (panned && isPresenter) {
|
if (panned && isPresenter) {
|
||||||
// // // limit bounds
|
// // limit bounds
|
||||||
// if (
|
if (
|
||||||
// editor?.viewportPageBounds?.maxX >
|
editor?.viewportPageBounds?.maxX >
|
||||||
// currentPresentationPage?.scaledWidth
|
currentPresentationPage?.scaledWidth
|
||||||
// ) {
|
) {
|
||||||
// next.x +=
|
next.x +=
|
||||||
// editor.viewportPageBounds.maxX -
|
editor.viewportPageBounds.maxX -
|
||||||
// currentPresentationPage?.scaledWidth;
|
currentPresentationPage?.scaledWidth;
|
||||||
// }
|
}
|
||||||
// if (
|
if (
|
||||||
// editor?.viewportPageBounds?.maxY >
|
editor?.viewportPageBounds?.maxY >
|
||||||
// currentPresentationPage?.scaledHeight
|
currentPresentationPage?.scaledHeight
|
||||||
// ) {
|
) {
|
||||||
// next.y +=
|
next.y +=
|
||||||
// editor.viewportPageBounds.maxY -
|
editor.viewportPageBounds.maxY -
|
||||||
// currentPresentationPage?.scaledHeight;
|
currentPresentationPage?.scaledHeight;
|
||||||
// }
|
}
|
||||||
// if (next.x > 0 || editor.viewportPageBounds.minX < 0) {
|
if (next.x > 0 || editor.viewportPageBounds.minX < 0) {
|
||||||
// next.x = 0;
|
next.x = 0;
|
||||||
// }
|
}
|
||||||
// if (next.y > 0 || editor.viewportPageBounds.minY < 0) {
|
if (next.y > 0 || editor.viewportPageBounds.minY < 0) {
|
||||||
// next.y = 0;
|
next.y = 0;
|
||||||
// }
|
}
|
||||||
// }
|
}
|
||||||
return next;
|
return next;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -44,6 +44,7 @@ const WhiteboardContainer = (props) => {
|
|||||||
intl,
|
intl,
|
||||||
slidePosition,
|
slidePosition,
|
||||||
svgUri,
|
svgUri,
|
||||||
|
zoomChanger,
|
||||||
} = props;
|
} = props;
|
||||||
|
|
||||||
const [annotations, setAnnotations] = useState([]);
|
const [annotations, setAnnotations] = useState([]);
|
||||||
@ -270,6 +271,7 @@ const WhiteboardContainer = (props) => {
|
|||||||
presentationId,
|
presentationId,
|
||||||
hasWBAccess,
|
hasWBAccess,
|
||||||
whiteboardWriters,
|
whiteboardWriters,
|
||||||
|
zoomChanger,
|
||||||
}}
|
}}
|
||||||
{...props}
|
{...props}
|
||||||
meetingId={Auth.meetingID}
|
meetingId={Auth.meetingID}
|
||||||
|
@ -1,4 +1,8 @@
|
|||||||
import React, { useState, useEffect, useRef } from 'react';
|
import React, { useState, useEffect, useRef } from 'react';
|
||||||
|
import {
|
||||||
|
HUNDRED_PERCENT,
|
||||||
|
MAX_PERCENT,
|
||||||
|
} from "/imports/utils/slideCalcUtils";
|
||||||
|
|
||||||
const useCursor = (publishCursorUpdate, whiteboardId) => {
|
const useCursor = (publishCursorUpdate, whiteboardId) => {
|
||||||
const [cursorPosition, setCursorPosition] = useState({ x: -1, y: -1 });
|
const [cursorPosition, setCursorPosition] = useState({ x: -1, y: -1 });
|
||||||
@ -18,7 +22,7 @@ const useCursor = (publishCursorUpdate, whiteboardId) => {
|
|||||||
return [cursorPosition, updateCursorPosition];
|
return [cursorPosition, updateCursorPosition];
|
||||||
};
|
};
|
||||||
|
|
||||||
const useMouseEvents = ({ whiteboardRef, tlEditorRef }, {
|
const useMouseEvents = ({ whiteboardRef, tlEditorRef, isWheelZoomRef, initialZoomRef }, {
|
||||||
isPresenter,
|
isPresenter,
|
||||||
hasWBAccess,
|
hasWBAccess,
|
||||||
isMouseDownRef,
|
isMouseDownRef,
|
||||||
@ -28,7 +32,9 @@ const useMouseEvents = ({ whiteboardRef, tlEditorRef }, {
|
|||||||
whiteboardId,
|
whiteboardId,
|
||||||
cursorPosition,
|
cursorPosition,
|
||||||
updateCursorPosition,
|
updateCursorPosition,
|
||||||
toggleToolsAnimations
|
toggleToolsAnimations,
|
||||||
|
currentPresentationPage,
|
||||||
|
zoomChanger,
|
||||||
}) => {
|
}) => {
|
||||||
|
|
||||||
const timeoutIdRef = React.useRef();
|
const timeoutIdRef = React.useRef();
|
||||||
@ -80,42 +86,72 @@ const useMouseEvents = ({ whiteboardRef, tlEditorRef }, {
|
|||||||
}, 150);
|
}, 150);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
const handleMouseWheel = (event) => {
|
const handleMouseWheel = (event) => {
|
||||||
|
event.preventDefault();
|
||||||
|
event.stopPropagation();
|
||||||
if (!tlEditorRef.current || !isPresenter) {
|
if (!tlEditorRef.current || !isPresenter) {
|
||||||
event.preventDefault();
|
|
||||||
event.stopPropagation();
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const MAX_ZOOM = 4;
|
isWheelZoomRef.current = true;
|
||||||
const MIN_ZOOM = .2;
|
|
||||||
const ZOOM_IN_FACTOR = 0.100; // Finer zoom control
|
const MAX_ZOOM_FACTOR = 4; // Represents 400%
|
||||||
const ZOOM_OUT_FACTOR = 0.100;
|
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;
|
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) {
|
if (event.deltaY < 0) {
|
||||||
// Zoom in
|
currentZoomLevel = Math.min(currentZoomLevel + ZOOM_IN_FACTOR, MAX_ZOOM_FACTOR);
|
||||||
zoom = Math.min(cz + ZOOM_IN_FACTOR, MAX_ZOOM);
|
|
||||||
} else {
|
} else {
|
||||||
// Zoom out
|
currentZoomLevel = Math.max(currentZoomLevel - ZOOM_OUT_FACTOR, MIN_ZOOM_FACTOR);
|
||||||
zoom = Math.max(cz - ZOOM_OUT_FACTOR, MIN_ZOOM);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const { x, y } = { x: cursorPosition?.x, y: cursorPosition?.y };
|
// Convert zoom level to a percentage for backend
|
||||||
|
const zoomPercentage = currentZoomLevel * 100;
|
||||||
|
zoomChanger(zoomPercentage);
|
||||||
|
|
||||||
|
// Calculate the new camera zoom factor
|
||||||
|
const newCameraZoomFactor = currentZoomLevel * initialZoomRef.current;
|
||||||
|
|
||||||
const nextCamera = {
|
const nextCamera = {
|
||||||
x: cx + (x / zoom - x) - (x / cz - x),
|
x: cx + (cursorPosition.x / newCameraZoomFactor - cursorPosition.x) - (cursorPosition.x / cz - cursorPosition.x),
|
||||||
y: cy + (y / zoom - y) - (y / cz - y),
|
y: cy + (cursorPosition.y / newCameraZoomFactor - cursorPosition.y) - (cursorPosition.y / cz - cursorPosition.y),
|
||||||
z: zoom,
|
z: newCameraZoomFactor,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// 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 });
|
tlEditorRef.current.setCamera(nextCamera, { duration: 300 });
|
||||||
|
|
||||||
event.preventDefault();
|
if (isWheelZoomRef.currentTimeout) {
|
||||||
event.stopPropagation();
|
clearTimeout(isWheelZoomRef.currentTimeout);
|
||||||
|
}
|
||||||
|
|
||||||
|
isWheelZoomRef.currentTimeout = setTimeout(() => {
|
||||||
|
isWheelZoomRef.current = false;
|
||||||
|
}, 300);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
React.useEffect(() => {
|
React.useEffect(() => {
|
||||||
if (whiteboardToolbarAutoHide) {
|
if (whiteboardToolbarAutoHide) {
|
||||||
toggleToolsAnimations(
|
toggleToolsAnimations(
|
||||||
@ -157,6 +193,8 @@ const useMouseEvents = ({ whiteboardRef, tlEditorRef }, {
|
|||||||
}, [whiteboardRef, tlEditorRef, handleMouseDown, handleMouseUp, handleMouseEnter, handleMouseLeave, handleMouseWheel]);
|
}, [whiteboardRef, tlEditorRef, handleMouseDown, handleMouseUp, handleMouseEnter, handleMouseLeave, handleMouseWheel]);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
export {
|
export {
|
||||||
useMouseEvents,
|
useMouseEvents,
|
||||||
useCursor,
|
useCursor,
|
||||||
|
Loading…
Reference in New Issue
Block a user