mirror of
https://github.com/excalidraw/excalidraw.git
synced 2025-05-03 10:00:07 -04:00
refactor: split constrainScroll into smaller functions
This commit is contained in:
parent
92be92071a
commit
e8e391e465
1 changed files with 179 additions and 119 deletions
|
@ -7675,109 +7675,146 @@ class App extends React.Component<AppProps, AppState> {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Calculate the zoom level on which will constrained area fit the viewport for each axis
|
/**
|
||||||
|
* Calculate the zoom levels on which will constrained area fits the viewport for each axis
|
||||||
|
* @returns The zoom levels for the X and Y axes.
|
||||||
|
*/
|
||||||
|
const calculateZoomLevel = () => {
|
||||||
const scrollableWidth = scrollConstraints.width;
|
const scrollableWidth = scrollConstraints.width;
|
||||||
const scrollableHeight = scrollConstraints.height;
|
const scrollableHeight = scrollConstraints.height;
|
||||||
const zoomLevelX = width / scrollableWidth;
|
const zoomLevelX = width / scrollableWidth;
|
||||||
const zoomLevelY = height / scrollableHeight;
|
const zoomLevelY = height / scrollableHeight;
|
||||||
|
return { zoomLevelX, zoomLevelY };
|
||||||
|
};
|
||||||
|
|
||||||
// Default scroll and zoom values
|
/**
|
||||||
let constrainedScrollX = scrollX;
|
* Calculates the center position of the constrained scroll area.
|
||||||
let constrainedScrollY = scrollY;
|
* @returns The X and Y coordinates of the center position.
|
||||||
|
*/
|
||||||
|
const calculateConstrainedScrollCenter = () => {
|
||||||
|
const constrainedScrollCenterX =
|
||||||
|
scrollConstraints.x +
|
||||||
|
(scrollConstraints.width - width / zoom.value) / -2;
|
||||||
|
const constrainedScrollCenterY =
|
||||||
|
scrollConstraints.y +
|
||||||
|
(scrollConstraints.height - height / zoom.value) / -2;
|
||||||
|
return { constrainedScrollCenterX, constrainedScrollCenterY };
|
||||||
|
};
|
||||||
|
|
||||||
// Calculate the overscroll allowance for each axis
|
/**
|
||||||
|
* Calculates the overscroll allowance values for the constrained area.
|
||||||
|
* @returns The overscroll allowance values for the X and Y axes.
|
||||||
|
*/
|
||||||
|
const calculateOverscrollAllowance = () => {
|
||||||
const overscrollAllowanceX =
|
const overscrollAllowanceX =
|
||||||
OVERSCROLL_ALLOWANCE_PERCENTAGE * scrollableWidth;
|
OVERSCROLL_ALLOWANCE_PERCENTAGE * scrollConstraints.width;
|
||||||
const overscrollAllowanceY =
|
const overscrollAllowanceY =
|
||||||
OVERSCROLL_ALLOWANCE_PERCENTAGE * scrollableHeight;
|
OVERSCROLL_ALLOWANCE_PERCENTAGE * scrollConstraints.height;
|
||||||
|
return { overscrollAllowanceX, overscrollAllowanceY };
|
||||||
|
};
|
||||||
|
|
||||||
// When we are zoomed out enough to contain constrained area in the viewport we will center the view
|
/**
|
||||||
const shouldAdjustForCenteredView =
|
* Calculates the minimum and maximum scroll values based on the current state.
|
||||||
zoom.value <= zoomLevelX || zoom.value <= zoomLevelY;
|
* @param shouldAdjustForCenteredView - Whether the view should be adjusted for centered view - when constrained area fits the viewport.
|
||||||
|
* @param overscrollAllowanceX - The overscroll allowance value for the X axis.
|
||||||
// When viewport is smaller than the scrollable area, user can pan freely within the constrained area,
|
* @param overscrollAllowanceY - The overscroll allowance value for the Y axis.
|
||||||
// otherwilse the viewport is centered to the center of the scrollable area
|
* @param constrainedScrollCenterX - The X coordinate of the constrained scroll area center.
|
||||||
|
* @param constrainedScrollCenterY - The Y coordinate of the constrained scroll area center.
|
||||||
|
* @returns The minimum and maximum scroll values for the X and Y axes.
|
||||||
|
*/
|
||||||
|
const calculateMinMaxScrollValues = (
|
||||||
|
shouldAdjustForCenteredView: boolean,
|
||||||
|
overscrollAllowanceX: number,
|
||||||
|
overscrollAllowanceY: number,
|
||||||
|
constrainedScrollCenterX: number,
|
||||||
|
constrainedScrollCenterY: number,
|
||||||
|
) => {
|
||||||
let maxScrollX;
|
let maxScrollX;
|
||||||
let minScrollX;
|
let minScrollX;
|
||||||
let maxScrollY;
|
let maxScrollY;
|
||||||
let minScrollY;
|
let minScrollY;
|
||||||
|
|
||||||
// Get center of scrollable area
|
if (cursorButton === "down" && shouldAdjustForCenteredView) {
|
||||||
const constrainedScrollCenterX =
|
|
||||||
scrollConstraints.x + (scrollableWidth - width / zoom.value) / -2;
|
|
||||||
const constrainedScrollCenterY =
|
|
||||||
scrollConstraints.y + (scrollableHeight - height / zoom.value) / -2;
|
|
||||||
|
|
||||||
// We're using a `switch(true)` construction here to handle a set of conditions
|
|
||||||
// that can't be easily grouped into a regular switch statement.
|
|
||||||
// Each case represents a unique combination of cursorButton state and
|
|
||||||
// whether or not we should adjust for a centered view (constrained area is smaller than viewport).
|
|
||||||
switch (true) {
|
|
||||||
case cursorButton === "down" && shouldAdjustForCenteredView:
|
|
||||||
// case when cursor button is down and we should adjust for centered view
|
|
||||||
|
|
||||||
maxScrollX = constrainedScrollCenterX + overscrollAllowanceX;
|
maxScrollX = constrainedScrollCenterX + overscrollAllowanceX;
|
||||||
minScrollX = constrainedScrollCenterX - overscrollAllowanceX;
|
minScrollX = constrainedScrollCenterX - overscrollAllowanceX;
|
||||||
|
|
||||||
maxScrollY = constrainedScrollCenterY + overscrollAllowanceY;
|
maxScrollY = constrainedScrollCenterY + overscrollAllowanceY;
|
||||||
minScrollY = constrainedScrollCenterY - overscrollAllowanceY;
|
minScrollY = constrainedScrollCenterY - overscrollAllowanceY;
|
||||||
break;
|
} else if (cursorButton === "down" && !shouldAdjustForCenteredView) {
|
||||||
|
|
||||||
case cursorButton === "down" && !shouldAdjustForCenteredView:
|
|
||||||
// case when cursor button is down and we should not adjust for centered view
|
|
||||||
maxScrollX = scrollConstraints.x + overscrollAllowanceX;
|
maxScrollX = scrollConstraints.x + overscrollAllowanceX;
|
||||||
minScrollX =
|
minScrollX =
|
||||||
scrollConstraints.x -
|
scrollConstraints.x -
|
||||||
scrollableWidth +
|
scrollConstraints.width +
|
||||||
width / zoom.value -
|
width / zoom.value -
|
||||||
overscrollAllowanceX;
|
overscrollAllowanceX;
|
||||||
|
|
||||||
maxScrollY = scrollConstraints.y + overscrollAllowanceY;
|
maxScrollY = scrollConstraints.y + overscrollAllowanceY;
|
||||||
minScrollY =
|
minScrollY =
|
||||||
scrollConstraints.y -
|
scrollConstraints.y -
|
||||||
scrollableHeight +
|
scrollConstraints.height +
|
||||||
height / zoom.value -
|
height / zoom.value -
|
||||||
overscrollAllowanceY;
|
overscrollAllowanceY;
|
||||||
break;
|
} else if (cursorButton !== "down" && shouldAdjustForCenteredView) {
|
||||||
|
|
||||||
case cursorButton !== "down" && shouldAdjustForCenteredView:
|
|
||||||
// case when cursor button is not down and we should adjust for centered view
|
|
||||||
|
|
||||||
maxScrollX = constrainedScrollCenterX;
|
maxScrollX = constrainedScrollCenterX;
|
||||||
minScrollX = constrainedScrollCenterX;
|
minScrollX = constrainedScrollCenterX;
|
||||||
|
|
||||||
maxScrollY = constrainedScrollCenterY;
|
maxScrollY = constrainedScrollCenterY;
|
||||||
minScrollY = constrainedScrollCenterY;
|
minScrollY = constrainedScrollCenterY;
|
||||||
break;
|
} else {
|
||||||
|
|
||||||
default:
|
|
||||||
// case when cursor button is not down and we should not adjust for centered view
|
|
||||||
maxScrollX = scrollConstraints.x;
|
maxScrollX = scrollConstraints.x;
|
||||||
minScrollX = scrollConstraints.x - scrollableWidth + width / zoom.value;
|
minScrollX =
|
||||||
|
scrollConstraints.x - scrollConstraints.width + width / zoom.value;
|
||||||
maxScrollY = scrollConstraints.y;
|
maxScrollY = scrollConstraints.y;
|
||||||
minScrollY =
|
minScrollY =
|
||||||
scrollConstraints.y - scrollableHeight + height / zoom.value;
|
scrollConstraints.y - scrollConstraints.height + height / zoom.value;
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Constrain the scroll within the scroll constraints
|
return { maxScrollX, minScrollX, maxScrollY, minScrollY };
|
||||||
constrainedScrollX = Math.min(maxScrollX, Math.max(scrollX, minScrollX));
|
};
|
||||||
constrainedScrollY = Math.min(maxScrollY, Math.max(scrollY, minScrollY));
|
|
||||||
|
|
||||||
// Check if the new state differs from the old state
|
/**
|
||||||
// and if the state has changed, update the state and return the new state
|
* Constrains the scroll values within the constrained area.
|
||||||
|
* @param maxScrollX - The maximum scroll value for the X axis.
|
||||||
|
* @param minScrollX - The minimum scroll value for the X axis.
|
||||||
|
* @param maxScrollY - The maximum scroll value for the Y axis.
|
||||||
|
* @param minScrollY - The minimum scroll value for the Y axis.
|
||||||
|
* @returns The constrained scroll values for the X and Y axes.
|
||||||
|
*/
|
||||||
|
const constrainScrollValues = (
|
||||||
|
maxScrollX: number,
|
||||||
|
minScrollX: number,
|
||||||
|
maxScrollY: number,
|
||||||
|
minScrollY: number,
|
||||||
|
) => {
|
||||||
|
const constrainedScrollX = Math.min(
|
||||||
|
maxScrollX,
|
||||||
|
Math.max(scrollX, minScrollX),
|
||||||
|
);
|
||||||
|
const constrainedScrollY = Math.min(
|
||||||
|
maxScrollY,
|
||||||
|
Math.max(scrollY, minScrollY),
|
||||||
|
);
|
||||||
|
return { constrainedScrollX, constrainedScrollY };
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handles the state change based on the constrained scroll values.
|
||||||
|
* Also handles the animation to the constrained area when the viewport is outside of constrained area.
|
||||||
|
* @param constrainedScrollX - The constrained scroll value for the X axis.
|
||||||
|
* @param constrainedScrollY - The constrained scroll value for the Y axis.
|
||||||
|
* @returns The constrained state if the state has changed, when needs to be passed into render function, otherwise null.
|
||||||
|
*/
|
||||||
|
const handleStateChange = (
|
||||||
|
constrainedScrollX: number,
|
||||||
|
constrainedScrollY: number,
|
||||||
|
) => {
|
||||||
const isStateChanged =
|
const isStateChanged =
|
||||||
constrainedScrollX !== scrollX || constrainedScrollY !== scrollY;
|
constrainedScrollX !== scrollX || constrainedScrollY !== scrollY;
|
||||||
|
|
||||||
if (isStateChanged) {
|
if (isStateChanged) {
|
||||||
// Animate the scroll position when the cursor button is not down and scroll position is outside of the scroll constraints.
|
|
||||||
// We don't want to animate the scroll position when the user is dragging the canvas or zooiming in/out.
|
|
||||||
if (
|
if (
|
||||||
(scrollX < scrollConstraints.x ||
|
(scrollX < scrollConstraints.x ||
|
||||||
scrollX + width > scrollConstraints.x + scrollConstraints.width ||
|
scrollX + width > scrollConstraints.x + scrollConstraints.width ||
|
||||||
scrollY < scrollConstraints.y ||
|
scrollY < scrollConstraints.y ||
|
||||||
scrollY + height > scrollConstraints.y + scrollConstraints.height) &&
|
scrollY + height >
|
||||||
|
scrollConstraints.y + scrollConstraints.height) &&
|
||||||
cursorButton !== "down" &&
|
cursorButton !== "down" &&
|
||||||
zoom.value === prevState.zoom.value
|
zoom.value === prevState.zoom.value
|
||||||
) {
|
) {
|
||||||
|
@ -7792,10 +7829,7 @@ class App extends React.Component<AppProps, AppState> {
|
||||||
scrollY: constrainedScrollY,
|
scrollY: constrainedScrollY,
|
||||||
},
|
},
|
||||||
onStep: ({ scrollX, scrollY }) => {
|
onStep: ({ scrollX, scrollY }) => {
|
||||||
this.setState({
|
this.setState({ scrollX, scrollY });
|
||||||
scrollX,
|
|
||||||
scrollY,
|
|
||||||
});
|
|
||||||
},
|
},
|
||||||
onStart: () => {
|
onStart: () => {
|
||||||
this.setState({
|
this.setState({
|
||||||
|
@ -7824,6 +7858,32 @@ class App extends React.Component<AppProps, AppState> {
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Compute the constrained scroll values.
|
||||||
|
const { zoomLevelX, zoomLevelY } = calculateZoomLevel();
|
||||||
|
const { constrainedScrollCenterX, constrainedScrollCenterY } =
|
||||||
|
calculateConstrainedScrollCenter();
|
||||||
|
const { overscrollAllowanceX, overscrollAllowanceY } =
|
||||||
|
calculateOverscrollAllowance();
|
||||||
|
const shouldAdjustForCenteredView =
|
||||||
|
zoom.value <= zoomLevelX || zoom.value <= zoomLevelY;
|
||||||
|
const { maxScrollX, minScrollX, maxScrollY, minScrollY } =
|
||||||
|
calculateMinMaxScrollValues(
|
||||||
|
shouldAdjustForCenteredView,
|
||||||
|
overscrollAllowanceX,
|
||||||
|
overscrollAllowanceY,
|
||||||
|
constrainedScrollCenterX,
|
||||||
|
constrainedScrollCenterY,
|
||||||
|
);
|
||||||
|
const { constrainedScrollX, constrainedScrollY } = constrainScrollValues(
|
||||||
|
maxScrollX,
|
||||||
|
minScrollX,
|
||||||
|
maxScrollY,
|
||||||
|
minScrollY,
|
||||||
|
);
|
||||||
|
|
||||||
|
return handleStateChange(constrainedScrollX, constrainedScrollY);
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
// -----------------------------------------------------------------------------
|
// -----------------------------------------------------------------------------
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue