feat: allow scroll over constraints while mouse down

This commit is contained in:
Arnošt Pleskot 2023-07-04 18:20:01 +02:00
parent f7e8056abe
commit 35b43c14d8
No known key found for this signature in database

View File

@ -1495,6 +1495,7 @@ class App extends React.Component<AppProps, AppState> {
} }
componentDidUpdate(prevProps: AppProps, prevState: AppState) { componentDidUpdate(prevProps: AppProps, prevState: AppState) {
console.log(this.state.cursorButton);
if ( if (
!this.state.showWelcomeScreen && !this.state.showWelcomeScreen &&
!this.scene.getElementsIncludingDeleted().length !this.scene.getElementsIncludingDeleted().length
@ -7639,86 +7640,116 @@ class App extends React.Component<AppProps, AppState> {
* @returns The modified next state with scrollX and scrollY constrained to the scroll constraints. * @returns The modified next state with scrollX and scrollY constrained to the scroll constraints.
*/ */
private constrainScroll = (prevState: AppState): ConstrainedScrollValues => { private constrainScroll = (prevState: AppState): ConstrainedScrollValues => {
const { scrollX, scrollY, scrollConstraints, width, height, zoom } = const {
this.state; scrollX,
scrollY,
scrollConstraints,
width,
height,
zoom,
cursorButton,
} = this.state;
// Skip if scroll constraints are not defined or if the zoom level or viewport dimensions have not changed. // Set the overscroll allowance percentage
// Constrains and scene will update on change of viewport dimensions. const OVERSCROLL_ALLOWANCE_PERCENTAGE = 0.1;
if (
!scrollConstraints || // Check if the state has changed since the last render
(this.state.zoom.value === prevState.zoom.value && const stateUnchanged =
this.state.scrollX === prevState.scrollX && zoom.value === prevState.zoom.value &&
this.state.scrollY === prevState.scrollY && scrollX === prevState.scrollX &&
this.state.width === prevState.width && scrollY === prevState.scrollY &&
this.state.height === prevState.height) width === prevState.width &&
) { height === prevState.height &&
cursorButton === prevState.cursorButton;
// If the state hasn't changed or the scroll constraints are not defined, return null
if (!scrollConstraints || stateUnchanged) {
return null; return null;
} }
// Calculate maximum zoom for both X and Y axis based on width and height of viewport and scrollable area // Calculate the maximum possible zoom based on the viewport and scrollable area sizes
const maxZoomX = width / scrollConstraints.width; const scrollableWidth = scrollConstraints.width;
const maxZoomY = height / scrollConstraints.height; const scrollableHeight = scrollConstraints.height;
const maxZoomX = width / scrollableWidth;
// The smallest zoom out of maxZoomX and maxZoomY is our zoom limit const maxZoomY = height / scrollableHeight;
const zoomLimit = Math.min(maxZoomX, maxZoomY); const zoomLimit = Math.min(maxZoomX, maxZoomY);
// Set default constrainedScrollX and constrainedScrollY values // Default scroll and zoom values
let constrainedScrollX = scrollX; let constrainedScrollX = scrollX;
let constrainedScrollY = scrollY; let constrainedScrollY = scrollY;
let constrainedZoom = zoom; const constrainedZoom = {
value: getNormalizedZoom(Math.max(zoom.value, zoomLimit)),
};
// Function to adjust scroll position for centered view depending on the zoom value // Calculate the overscroll allowance for each axis
const overscrollAllowanceX =
OVERSCROLL_ALLOWANCE_PERCENTAGE * scrollableWidth;
const overscrollAllowanceY =
OVERSCROLL_ALLOWANCE_PERCENTAGE * scrollableHeight;
// Define the maximum and minimum scroll for each axis based on the cursor button state
const maxScrollX =
cursorButton === "down"
? scrollConstraints.x + overscrollAllowanceX
: scrollConstraints.x;
const minScrollX =
cursorButton === "down"
? scrollConstraints.x -
scrollableWidth +
width / zoom.value -
overscrollAllowanceX
: scrollConstraints.x - scrollableWidth + width / zoom.value;
const maxScrollY =
cursorButton === "down"
? scrollConstraints.y + overscrollAllowanceY
: scrollConstraints.y;
const minScrollY =
cursorButton === "down"
? scrollConstraints.y -
scrollableHeight +
height / zoom.value -
overscrollAllowanceY
: scrollConstraints.y - scrollableHeight + height / zoom.value;
// Constrain the scroll within the scroll constraints, with overscroll allowance if the cursor button is down
constrainedScrollX = Math.min(maxScrollX, Math.max(scrollX, minScrollX));
constrainedScrollY = Math.min(maxScrollY, Math.max(scrollY, minScrollY));
// Check if the zoom value requires adjustment for a centered view
const shouldAdjustForCenteredView =
zoom.value <= maxZoomX || zoom.value <= maxZoomY;
if (shouldAdjustForCenteredView) {
// Adjust the scroll position for a centered view based on the zoom value
const adjustScrollForCenteredView = (zoomValue: number) => { const adjustScrollForCenteredView = (zoomValue: number) => {
// If zoom value is less than or equal to maxZoomX, adjust scrollX to ensure the view is centered on the X axis
if (zoomValue <= maxZoomX) { if (zoomValue <= maxZoomX) {
const centeredScrollX = const centeredScrollX = (scrollableWidth - width / zoomValue) / -2;
(scrollConstraints.width - width / zoomValue) / -2;
constrainedScrollX = scrollConstraints.x + centeredScrollX; constrainedScrollX = scrollConstraints.x + centeredScrollX;
} }
// If zoom value is less than or equal to maxZoomY, adjust scrollY to ensure the view is centered on the Y axis
if (zoomValue <= maxZoomY) { if (zoomValue <= maxZoomY) {
const centeredScrollY = const centeredScrollY = (scrollableHeight - height / zoomValue) / -2;
(scrollConstraints.height - height / zoomValue) / -2;
constrainedScrollY = scrollConstraints.y + centeredScrollY; constrainedScrollY = scrollConstraints.y + centeredScrollY;
} }
}; };
// Constrain scrollX and scrollY within the scroll constraints
constrainedScrollX = Math.min(
scrollConstraints.x,
Math.max(
scrollX,
scrollConstraints.x - scrollConstraints.width + width / zoom.value,
),
);
constrainedScrollY = Math.min(
scrollConstraints.y,
Math.max(
scrollY,
scrollConstraints.y - scrollConstraints.height + height / zoom.value,
),
);
// Constrain zoom within the scroll constraints and adjust for centered view
constrainedZoom = {
value: getNormalizedZoom(Math.max(zoom.value, zoomLimit)),
};
adjustScrollForCenteredView(zoom.value); adjustScrollForCenteredView(zoom.value);
}
// If any of the values have changed, set new state // Check if the new state differs from the old state
if ( const stateChanged =
constrainedScrollX !== this.state.scrollX || constrainedScrollX !== scrollX ||
constrainedScrollY !== this.state.scrollY || constrainedScrollY !== scrollY ||
constrainedZoom.value !== this.state.zoom.value constrainedZoom.value !== zoom.value;
) {
// If the state has changed, update the state and return the new state
if (stateChanged) {
const constrainedState = { const constrainedState = {
scrollX: constrainedScrollX, scrollX: constrainedScrollX,
scrollY: constrainedScrollY, scrollY: constrainedScrollY,
zoom: constrainedZoom, zoom: constrainedZoom,
}; };
this.setState(constrainedState); this.setState(constrainedState);
return constrainedState; return constrainedState;
} }