feat: add zoom lock and viewportZoomFactor

This commit is contained in:
Arnošt Pleskot 2023-07-10 18:10:22 +02:00
parent e8e391e465
commit 9562e4309f
No known key found for this signature in database
2 changed files with 43 additions and 21 deletions

View file

@ -223,6 +223,7 @@ import {
FrameNameBoundsCache, FrameNameBoundsCache,
SidebarName, SidebarName,
SidebarTabName, SidebarTabName,
NormalizedZoomValue,
} from "../types"; } from "../types";
import { import {
debounce, debounce,
@ -7650,20 +7651,17 @@ class App extends React.Component<AppProps, AppState> {
scrollConstraints, scrollConstraints,
width, width,
height, height,
zoom, zoom: currentZoom,
cursorButton, cursorButton,
} = this.state; } = this.state;
// Set the overscroll allowance percentage
const OVERSCROLL_ALLOWANCE_PERCENTAGE = 0.2;
if (!scrollConstraints || scrollConstraints.isAnimating) { if (!scrollConstraints || scrollConstraints.isAnimating) {
return null; return null;
} }
// Check if the state has changed since the last render // Check if the state has changed since the last render
const stateUnchanged = const stateUnchanged =
zoom.value === prevState.zoom.value && currentZoom.value === prevState.zoom.value &&
scrollX === prevState.scrollX && scrollX === prevState.scrollX &&
scrollY === prevState.scrollY && scrollY === prevState.scrollY &&
width === prevState.width && width === prevState.width &&
@ -7671,10 +7669,17 @@ class App extends React.Component<AppProps, AppState> {
cursorButton === prevState.cursorButton; cursorButton === prevState.cursorButton;
// If the state hasn't changed and scrollConstraints didn't just get defined, return null // If the state hasn't changed and scrollConstraints didn't just get defined, return null
if (!scrollConstraints || (stateUnchanged && prevState.scrollConstraints)) { if (stateUnchanged && prevState.scrollConstraints) {
return null; return null;
} }
// Set the overscroll allowance percentage
const OVERSCROLL_ALLOWANCE_PERCENTAGE = 0.2;
const lockZoom = scrollConstraints.opts?.lockZoom ?? false;
const viewportZoomFactor = scrollConstraints.opts?.viewportZoomFactor
? Math.min(1, Math.max(scrollConstraints.opts.viewportZoomFactor, 0.1))
: 0.9;
/** /**
* Calculate the zoom levels on which will constrained area fits 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. * @returns The zoom levels for the X and Y axes.
@ -7684,20 +7689,21 @@ class App extends React.Component<AppProps, AppState> {
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 }; const maxZoomLevel = lockZoom
? Math.min(zoomLevelX, zoomLevelY) * viewportZoomFactor
: null;
return { zoomLevelX, zoomLevelY, maxZoomLevel };
}; };
/** /**
* Calculates the center position of the constrained scroll area. * Calculates the center position of the constrained scroll area.
* @returns The X and Y coordinates of the center position. * @returns The X and Y coordinates of the center position.
*/ */
const calculateConstrainedScrollCenter = () => { const calculateConstrainedScrollCenter = (zoom: number) => {
const constrainedScrollCenterX = const constrainedScrollCenterX =
scrollConstraints.x + scrollConstraints.x + (scrollConstraints.width - width / zoom) / -2;
(scrollConstraints.width - width / zoom.value) / -2;
const constrainedScrollCenterY = const constrainedScrollCenterY =
scrollConstraints.y + scrollConstraints.y + (scrollConstraints.height - height / zoom) / -2;
(scrollConstraints.height - height / zoom.value) / -2;
return { constrainedScrollCenterX, constrainedScrollCenterY }; return { constrainedScrollCenterX, constrainedScrollCenterY };
}; };
@ -7728,6 +7734,7 @@ class App extends React.Component<AppProps, AppState> {
overscrollAllowanceY: number, overscrollAllowanceY: number,
constrainedScrollCenterX: number, constrainedScrollCenterX: number,
constrainedScrollCenterY: number, constrainedScrollCenterY: number,
zoom: number,
) => { ) => {
let maxScrollX; let maxScrollX;
let minScrollX; let minScrollX;
@ -7744,13 +7751,13 @@ class App extends React.Component<AppProps, AppState> {
minScrollX = minScrollX =
scrollConstraints.x - scrollConstraints.x -
scrollConstraints.width + scrollConstraints.width +
width / zoom.value - width / zoom -
overscrollAllowanceX; overscrollAllowanceX;
maxScrollY = scrollConstraints.y + overscrollAllowanceY; maxScrollY = scrollConstraints.y + overscrollAllowanceY;
minScrollY = minScrollY =
scrollConstraints.y - scrollConstraints.y -
scrollConstraints.height + scrollConstraints.height +
height / zoom.value - height / zoom -
overscrollAllowanceY; overscrollAllowanceY;
} else if (cursorButton !== "down" && shouldAdjustForCenteredView) { } else if (cursorButton !== "down" && shouldAdjustForCenteredView) {
maxScrollX = constrainedScrollCenterX; maxScrollX = constrainedScrollCenterX;
@ -7760,10 +7767,10 @@ class App extends React.Component<AppProps, AppState> {
} else { } else {
maxScrollX = scrollConstraints.x; maxScrollX = scrollConstraints.x;
minScrollX = minScrollX =
scrollConstraints.x - scrollConstraints.width + width / zoom.value; scrollConstraints.x - scrollConstraints.width + width / zoom;
maxScrollY = scrollConstraints.y; maxScrollY = scrollConstraints.y;
minScrollY = minScrollY =
scrollConstraints.y - scrollConstraints.height + height / zoom.value; scrollConstraints.y - scrollConstraints.height + height / zoom;
} }
return { maxScrollX, minScrollX, maxScrollY, minScrollY }; return { maxScrollX, minScrollX, maxScrollY, minScrollY };
@ -7816,7 +7823,7 @@ class App extends React.Component<AppProps, AppState> {
scrollY + height > scrollY + height >
scrollConstraints.y + scrollConstraints.height) && scrollConstraints.y + scrollConstraints.height) &&
cursorButton !== "down" && cursorButton !== "down" &&
zoom.value === prevState.zoom.value currentZoom.value === prevState.zoom.value
) { ) {
this.setState({ this.setState({
scrollConstraints: { ...scrollConstraints, isAnimating: true }, scrollConstraints: { ...scrollConstraints, isAnimating: true },
@ -7849,7 +7856,14 @@ class App extends React.Component<AppProps, AppState> {
const constrainedState = { const constrainedState = {
scrollX: constrainedScrollX, scrollX: constrainedScrollX,
scrollY: constrainedScrollY, scrollY: constrainedScrollY,
zoom, zoom: {
value: maxZoomLevel
? (Math.max(
currentZoom.value,
maxZoomLevel,
) as NormalizedZoomValue)
: currentZoom.value,
},
}; };
this.setState(constrainedState); this.setState(constrainedState);
@ -7860,13 +7874,16 @@ class App extends React.Component<AppProps, AppState> {
}; };
// Compute the constrained scroll values. // Compute the constrained scroll values.
const { zoomLevelX, zoomLevelY } = calculateZoomLevel(); const { zoomLevelX, zoomLevelY, maxZoomLevel } = calculateZoomLevel();
const zoom = maxZoomLevel
? Math.max(maxZoomLevel, currentZoom.value)
: currentZoom.value;
const { constrainedScrollCenterX, constrainedScrollCenterY } = const { constrainedScrollCenterX, constrainedScrollCenterY } =
calculateConstrainedScrollCenter(); calculateConstrainedScrollCenter(zoom);
const { overscrollAllowanceX, overscrollAllowanceY } = const { overscrollAllowanceX, overscrollAllowanceY } =
calculateOverscrollAllowance(); calculateOverscrollAllowance();
const shouldAdjustForCenteredView = const shouldAdjustForCenteredView =
zoom.value <= zoomLevelX || zoom.value <= zoomLevelY; zoom <= zoomLevelX || zoom <= zoomLevelY;
const { maxScrollX, minScrollX, maxScrollY, minScrollY } = const { maxScrollX, minScrollX, maxScrollY, minScrollY } =
calculateMinMaxScrollValues( calculateMinMaxScrollValues(
shouldAdjustForCenteredView, shouldAdjustForCenteredView,
@ -7874,6 +7891,7 @@ class App extends React.Component<AppProps, AppState> {
overscrollAllowanceY, overscrollAllowanceY,
constrainedScrollCenterX, constrainedScrollCenterX,
constrainedScrollCenterY, constrainedScrollCenterY,
zoom,
); );
const { constrainedScrollX, constrainedScrollY } = constrainScrollValues( const { constrainedScrollX, constrainedScrollY } = constrainScrollValues(
maxScrollX, maxScrollX,

View file

@ -229,6 +229,10 @@ export type AppState = {
width: number; width: number;
height: number; height: number;
isAnimating?: boolean; isAnimating?: boolean;
opts?: {
viewportZoomFactor?: number;
lockZoom?: boolean;
};
} | null; } | null;
}; };