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,
|
||||
ShapeStylesContext,
|
||||
hideViewersCursor,
|
||||
presentationHeight,
|
||||
presentationWidth,
|
||||
} = props;
|
||||
|
||||
clearTldrawCache();
|
||||
@ -163,6 +165,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);
|
||||
@ -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(
|
||||
{ whiteboardRef, tlEditorRef },
|
||||
{ whiteboardRef, tlEditorRef, isWheelZoomRef, initialZoomRef },
|
||||
{
|
||||
isPresenter,
|
||||
hasWBAccess,
|
||||
@ -291,6 +319,8 @@ export default Whiteboard = React.memo(function Whiteboard(props) {
|
||||
cursorPosition,
|
||||
updateCursorPosition,
|
||||
toggleToolsAnimations,
|
||||
currentPresentationPage,
|
||||
zoomChanger,
|
||||
}
|
||||
);
|
||||
|
||||
@ -299,46 +329,87 @@ export default Whiteboard = React.memo(function Whiteboard(props) {
|
||||
tlEditorRef.current = tlEditor;
|
||||
}, [tlEditor]);
|
||||
|
||||
// presenter effect to handle zoomSlide
|
||||
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
|
||||
);
|
||||
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,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Update the previous zoom value ref with the current zoom value
|
||||
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(() => {
|
||||
// Calculate the absolute difference
|
||||
@ -423,10 +494,6 @@ 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]);
|
||||
@ -641,31 +708,6 @@ export default Whiteboard = React.memo(function Whiteboard(props) {
|
||||
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);
|
||||
|
||||
@ -839,31 +881,31 @@ 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;
|
||||
};
|
||||
}
|
||||
|
@ -44,6 +44,7 @@ const WhiteboardContainer = (props) => {
|
||||
intl,
|
||||
slidePosition,
|
||||
svgUri,
|
||||
zoomChanger,
|
||||
} = props;
|
||||
|
||||
const [annotations, setAnnotations] = useState([]);
|
||||
@ -270,6 +271,7 @@ const WhiteboardContainer = (props) => {
|
||||
presentationId,
|
||||
hasWBAccess,
|
||||
whiteboardWriters,
|
||||
zoomChanger,
|
||||
}}
|
||||
{...props}
|
||||
meetingId={Auth.meetingID}
|
||||
|
@ -1,4 +1,8 @@
|
||||
import React, { useState, useEffect, useRef } from 'react';
|
||||
import {
|
||||
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,9 @@ const useMouseEvents = ({ whiteboardRef, tlEditorRef }, {
|
||||
whiteboardId,
|
||||
cursorPosition,
|
||||
updateCursorPosition,
|
||||
toggleToolsAnimations
|
||||
toggleToolsAnimations,
|
||||
currentPresentationPage,
|
||||
zoomChanger,
|
||||
}) => {
|
||||
|
||||
const timeoutIdRef = React.useRef();
|
||||
@ -80,42 +86,72 @@ const useMouseEvents = ({ whiteboardRef, tlEditorRef }, {
|
||||
}, 150);
|
||||
};
|
||||
|
||||
|
||||
const handleMouseWheel = (event) => {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
if (!tlEditorRef.current || !isPresenter) {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
return;
|
||||
}
|
||||
|
||||
const MAX_ZOOM = 4;
|
||||
const MIN_ZOOM = .2;
|
||||
const ZOOM_IN_FACTOR = 0.100; // Finer zoom control
|
||||
const ZOOM_OUT_FACTOR = 0.100;
|
||||
isWheelZoomRef.current = true;
|
||||
|
||||
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 in
|
||||
zoom = Math.min(cz + ZOOM_IN_FACTOR, MAX_ZOOM);
|
||||
currentZoomLevel = Math.min(currentZoomLevel + ZOOM_IN_FACTOR, MAX_ZOOM_FACTOR);
|
||||
} else {
|
||||
// Zoom out
|
||||
zoom = Math.max(cz - ZOOM_OUT_FACTOR, MIN_ZOOM);
|
||||
currentZoomLevel = Math.max(currentZoomLevel - ZOOM_OUT_FACTOR, MIN_ZOOM_FACTOR);
|
||||
}
|
||||
|
||||
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 = {
|
||||
x: cx + (x / zoom - x) - (x / cz - x),
|
||||
y: cy + (y / zoom - y) - (y / cz - 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
|
||||
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();
|
||||
if (isWheelZoomRef.currentTimeout) {
|
||||
clearTimeout(isWheelZoomRef.currentTimeout);
|
||||
}
|
||||
|
||||
isWheelZoomRef.currentTimeout = setTimeout(() => {
|
||||
isWheelZoomRef.current = false;
|
||||
}, 300);
|
||||
};
|
||||
|
||||
|
||||
React.useEffect(() => {
|
||||
if (whiteboardToolbarAutoHide) {
|
||||
toggleToolsAnimations(
|
||||
@ -157,6 +193,8 @@ const useMouseEvents = ({ whiteboardRef, tlEditorRef }, {
|
||||
}, [whiteboardRef, tlEditorRef, handleMouseDown, handleMouseUp, handleMouseEnter, handleMouseLeave, handleMouseWheel]);
|
||||
};
|
||||
|
||||
|
||||
|
||||
export {
|
||||
useMouseEvents,
|
||||
useCursor,
|
||||
|
Loading…
Reference in New Issue
Block a user