diff --git a/packages/excalidraw/actions/actionCanvas.tsx b/packages/excalidraw/actions/actionCanvas.tsx index 73f4864be..42213877d 100644 --- a/packages/excalidraw/actions/actionCanvas.tsx +++ b/packages/excalidraw/actions/actionCanvas.tsx @@ -100,18 +100,21 @@ export const actionZoomIn = register({ trackEvent: { category: "canvas" }, perform: (_elements, appState, _, app) => { return { - appState: { - ...appState, - ...getStateForZoom( - { - viewportX: appState.width / 2 + appState.offsetLeft, - viewportY: appState.height / 2 + appState.offsetTop, - nextZoom: getNormalizedZoom(appState.zoom.value + ZOOM_STEP), - }, - appState, - ), - userToFollow: null, - }, + appState: constrainScrollState( + { + ...appState, + ...getStateForZoom( + { + viewportX: appState.width / 2 + appState.offsetLeft, + viewportY: appState.height / 2 + appState.offsetTop, + nextZoom: getNormalizedZoom(appState.zoom.value + ZOOM_STEP), + }, + appState, + ), + userToFollow: null, + }, + { disableAnimation: true }, + ), commitToHistory: false, }; }, @@ -138,18 +141,21 @@ export const actionZoomOut = register({ trackEvent: { category: "canvas" }, perform: (_elements, appState, _, app) => { return { - appState: constrainScrollState({ - ...appState, - ...getStateForZoom( - { - viewportX: appState.width / 2 + appState.offsetLeft, - viewportY: appState.height / 2 + appState.offsetTop, - nextZoom: getNormalizedZoom(appState.zoom.value - ZOOM_STEP), - }, - appState, - ), - userToFollow: null, - }), + appState: constrainScrollState( + { + ...appState, + ...getStateForZoom( + { + viewportX: appState.width / 2 + appState.offsetLeft, + viewportY: appState.height / 2 + appState.offsetTop, + nextZoom: getNormalizedZoom(appState.zoom.value - ZOOM_STEP), + }, + appState, + ), + userToFollow: null, + }, + { disableAnimation: true }, + ), commitToHistory: false, }; }, diff --git a/packages/excalidraw/components/App.tsx b/packages/excalidraw/components/App.tsx index 5a7d06c17..0539df14a 100644 --- a/packages/excalidraw/components/App.tsx +++ b/packages/excalidraw/components/App.tsx @@ -2797,7 +2797,9 @@ class App extends React.Component { } if (this.state.scrollConstraints?.animateOnNextUpdate) { - const newState = constrainScrollState(this.state, false); + const newState = constrainScrollState(this.state, { + allowOverscroll: false, + }); scrollConstraintsAnimationTimeout = setTimeout(() => { this.cancelInProgressAnimation?.(); @@ -4282,7 +4284,7 @@ class App extends React.Component { state, ), }, - false, + { disableAnimation: true }, ), ); } @@ -9646,7 +9648,7 @@ class App extends React.Component { ...this.state, scrollConstraints, }, - false, + { allowOverscroll: false }, ); this.animateToConstrainedArea( diff --git a/packages/excalidraw/scene/scrollConstraints.ts b/packages/excalidraw/scene/scrollConstraints.ts index 32791d7ee..249743522 100644 --- a/packages/excalidraw/scene/scrollConstraints.ts +++ b/packages/excalidraw/scene/scrollConstraints.ts @@ -348,15 +348,17 @@ let memoizedValues: { allowOverscroll: boolean; } | null = null; +type Options = { allowOverscroll?: boolean; disableAnimation?: boolean }; /** * Constrains the AppState scroll values within the defined scroll constraints. * * @param state - The original AppState with the current scroll position, dimensions, and constraints. + * @param options - An object containing options for the method: allowOverscroll and disableAnimation. * @returns A new AppState object with scroll values constrained as per the defined constraints. */ export const constrainScrollState = ( state: AppState, - allowOverscroll = true, + options?: Options, ): AppState => { if (!state.scrollConstraints) { return state; @@ -370,6 +372,8 @@ export const constrainScrollState = ( zoom, } = state; + const { allowOverscroll = true, disableAnimation = false } = options || {}; + const scrollConstraints = alignScrollConstraints(inverseScrollConstraints); const canUseMemoizedValues = @@ -430,7 +434,9 @@ export const constrainScrollState = ( ...state, scrollConstraints: { ...state.scrollConstraints, - animateOnNextUpdate: isViewportOutsideOfConstrainedArea(state), + animateOnNextUpdate: disableAnimation + ? false + : isViewportOutsideOfConstrainedArea(state), }, ...constrainedValues, };