fix jumping/flashing when zooming in or out too quickly

This commit is contained in:
Ryan Di 2025-05-09 13:00:56 +10:00
parent bc6cc83b1e
commit 84c396aec2
2 changed files with 42 additions and 19 deletions

View File

@ -531,6 +531,7 @@ import type { Action, ActionResult } from "../actions/types";
import { import {
constrainScrollState, constrainScrollState,
calculateConstrainedScrollCenter, calculateConstrainedScrollCenter,
areCanvasViewsClose,
} from "../scene/scrollConstraints"; } from "../scene/scrollConstraints";
const AppContext = React.createContext<AppClassProperties>(null!); const AppContext = React.createContext<AppClassProperties>(null!);
@ -2975,20 +2976,27 @@ class App extends React.Component<AppProps, AppState> {
const newState = constrainScrollState(this.state, { const newState = constrainScrollState(this.state, {
allowOverscroll: false, allowOverscroll: false,
}); });
const fromValues = {
scrollX: this.state.scrollX,
scrollY: this.state.scrollY,
zoom: this.state.zoom.value,
};
const toValues = {
scrollX: newState.scrollX,
scrollY: newState.scrollY,
zoom: newState.zoom.value,
};
if (areCanvasViewsClose(fromValues, toValues)) {
return;
}
if (scrollConstraintsAnimationTimeout) {
clearTimeout(scrollConstraintsAnimationTimeout);
}
scrollConstraintsAnimationTimeout = setTimeout(() => { scrollConstraintsAnimationTimeout = setTimeout(() => {
this.cancelInProgressAnimation?.(); this.cancelInProgressAnimation?.();
const fromValues = {
scrollX: this.state.scrollX,
scrollY: this.state.scrollY,
zoom: this.state.zoom.value,
};
const toValues = {
scrollX: newState.scrollX,
scrollY: newState.scrollY,
zoom: newState.zoom.value,
};
this.animateToConstrainedArea(fromValues, toValues); this.animateToConstrainedArea(fromValues, toValues);
}, 200); }, 200);
} }

View File

@ -1,5 +1,9 @@
import { isShallowEqual } from "@excalidraw/common"; import { isShallowEqual } from "@excalidraw/common";
import { AppState, ScrollConstraints } from "../types"; import {
AnimateTranslateCanvasValues,
AppState,
ScrollConstraints,
} from "../types";
import { getNormalizedZoom } from "./normalize"; import { getNormalizedZoom } from "./normalize";
/** /**
@ -18,14 +22,13 @@ import { getNormalizedZoom } from "./normalize";
* *
* const { scrollX, scrollY, zoom } = this.calculateConstrainedScrollCenter(scrollConstraints, { scrollX, scrollY }); * const { scrollX, scrollY, zoom } = this.calculateConstrainedScrollCenter(scrollConstraints, { scrollX, scrollY });
*/ */
type CanvasTranslate = Pick<AppState, "scrollX" | "scrollY" | "zoom">;
export const calculateConstrainedScrollCenter = ( export const calculateConstrainedScrollCenter = (
state: AppState, state: AppState,
{ scrollX, scrollY }: Pick<AppState, "scrollX" | "scrollY">, { scrollX, scrollY }: Pick<AppState, "scrollX" | "scrollY">,
): { ): CanvasTranslate => {
scrollX: AppState["scrollX"];
scrollY: AppState["scrollY"];
zoom: AppState["zoom"];
} => {
const { const {
width, width,
height, height,
@ -352,7 +355,7 @@ const constrainScrollValues = ({
maxScrollY: number; maxScrollY: number;
minScrollY: number; minScrollY: number;
constrainedZoom: AppState["zoom"]; constrainedZoom: AppState["zoom"];
}) => { }): CanvasTranslate => {
const constrainedScrollX = Math.min( const constrainedScrollX = Math.min(
maxScrollX, maxScrollX,
Math.max(scrollX, minScrollX), Math.max(scrollX, minScrollX),
@ -389,7 +392,7 @@ const alignScrollConstraints = (
* Determines whether the current viewport is outside the constrained area defined in the AppState. * Determines whether the current viewport is outside the constrained area defined in the AppState.
* *
* @param state - The application state containing scroll, zoom, and constraint information. * @param state - The application state containing scroll, zoom, and constraint information.
* @returns True if the viewport is outside the constrained area; otherwise undefined. * @returns True if the viewport is outside the constrained area, false otherwise.
*/ */
const isViewportOutsideOfConstrainedArea = (state: AppState) => { const isViewportOutsideOfConstrainedArea = (state: AppState) => {
if (!state.scrollConstraints) { if (!state.scrollConstraints) {
@ -521,3 +524,15 @@ export const constrainScrollState = (
...constrainedValues, ...constrainedValues,
}; };
}; };
export const areCanvasViewsClose = (
from: AnimateTranslateCanvasValues,
to: AnimateTranslateCanvasValues,
): boolean => {
const threshold = 0.1; // Adjust based on your needs
return (
Math.abs(from.scrollX - to.scrollX) < threshold &&
Math.abs(from.scrollY - to.scrollY) < threshold &&
Math.abs(from.zoom - to.zoom) < threshold
);
};