refactor: split constrainScroll into smaller functions

This commit is contained in:
Arnošt Pleskot 2023-07-09 23:50:59 +02:00
parent 92be92071a
commit e8e391e465
No known key found for this signature in database

View File

@ -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);
};
} }
// ----------------------------------------------------------------------------- // -----------------------------------------------------------------------------