mirror of
https://github.com/excalidraw/excalidraw
synced 2025-07-25 13:58:22 +08:00
feat: experiment with a diff shift locking behavior
This commit is contained in:
@ -124,6 +124,11 @@ export const getDefaultAppState = (): Omit<
|
|||||||
searchMatches: null,
|
searchMatches: null,
|
||||||
lockedMultiSelections: {},
|
lockedMultiSelections: {},
|
||||||
activeLockedId: null,
|
activeLockedId: null,
|
||||||
|
cropPositionMovement: {
|
||||||
|
enabled: false,
|
||||||
|
croppingElementId: undefined,
|
||||||
|
directionLock: null,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -249,6 +254,7 @@ const APP_STATE_STORAGE_CONF = (<
|
|||||||
searchMatches: { browser: false, export: false, server: false },
|
searchMatches: { browser: false, export: false, server: false },
|
||||||
lockedMultiSelections: { browser: true, export: true, server: true },
|
lockedMultiSelections: { browser: true, export: true, server: true },
|
||||||
activeLockedId: { browser: false, export: false, server: false },
|
activeLockedId: { browser: false, export: false, server: false },
|
||||||
|
cropPositionMovement: { browser: false, export: false, server: false },
|
||||||
});
|
});
|
||||||
|
|
||||||
const _clearAppStateForStorage = <
|
const _clearAppStateForStorage = <
|
||||||
|
@ -6994,6 +6994,8 @@ class App extends React.Component<AppProps, AppState> {
|
|||||||
},
|
},
|
||||||
cropPositionMovement: {
|
cropPositionMovement: {
|
||||||
enabled: false,
|
enabled: false,
|
||||||
|
croppingElementId: undefined,
|
||||||
|
directionLock: null,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -8134,20 +8136,47 @@ class App extends React.Component<AppProps, AppState> {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// apply shift key constraint for directional movement
|
// apply shift key constraint for directional movement
|
||||||
|
const threshold = 20;
|
||||||
|
let snappingToOrigin = false;
|
||||||
if (event.shiftKey) {
|
if (event.shiftKey) {
|
||||||
const distanceX = Math.abs(totalDragOffset.x);
|
if (!pointerDownState.cropPositionMovement.directionLock) {
|
||||||
const distanceY = Math.abs(totalDragOffset.y);
|
if (
|
||||||
|
Math.abs(totalDragOffset.x) > threshold ||
|
||||||
const lockX = distanceX < distanceY;
|
Math.abs(totalDragOffset.y) > threshold
|
||||||
const lockY = distanceX > distanceY;
|
) {
|
||||||
|
pointerDownState.cropPositionMovement.directionLock =
|
||||||
if (lockX) {
|
Math.abs(totalDragOffset.x) > Math.abs(totalDragOffset.y)
|
||||||
totalDragOffset.x = 0;
|
? "x"
|
||||||
|
: "y";
|
||||||
|
} else {
|
||||||
|
// if within threshold and not locked, always snap to origin
|
||||||
|
snappingToOrigin = true;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// if user moves back within threshold, unlock and snap back
|
||||||
|
if (
|
||||||
|
Math.abs(totalDragOffset.x) < threshold &&
|
||||||
|
Math.abs(totalDragOffset.y) < threshold
|
||||||
|
) {
|
||||||
|
pointerDownState.cropPositionMovement.directionLock = null;
|
||||||
|
snappingToOrigin = true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
pointerDownState.cropPositionMovement.directionLock = null;
|
||||||
|
}
|
||||||
|
|
||||||
if (lockY) {
|
if (snappingToOrigin) {
|
||||||
totalDragOffset.y = 0;
|
totalDragOffset.x = 0;
|
||||||
}
|
totalDragOffset.y = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pointerDownState.cropPositionMovement.directionLock === "x") {
|
||||||
|
totalDragOffset.y = 0;
|
||||||
|
} else if (
|
||||||
|
pointerDownState.cropPositionMovement.directionLock === "y"
|
||||||
|
) {
|
||||||
|
totalDragOffset.x = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// scale the drag offset
|
// scale the drag offset
|
||||||
|
@ -444,6 +444,11 @@ export interface AppState {
|
|||||||
// as elements are unlocked, we remove the groupId from the elements
|
// as elements are unlocked, we remove the groupId from the elements
|
||||||
// and also remove groupId from this map
|
// and also remove groupId from this map
|
||||||
lockedMultiSelections: { [groupId: string]: true };
|
lockedMultiSelections: { [groupId: string]: true };
|
||||||
|
cropPositionMovement: {
|
||||||
|
croppingElementId?: string;
|
||||||
|
enabled: boolean;
|
||||||
|
directionLock: "x" | "y" | null;
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export type SearchMatch = {
|
export type SearchMatch = {
|
||||||
@ -800,6 +805,7 @@ export type PointerDownState = Readonly<{
|
|||||||
cropPositionMovement: {
|
cropPositionMovement: {
|
||||||
croppingElementId?: string;
|
croppingElementId?: string;
|
||||||
enabled: boolean;
|
enabled: boolean;
|
||||||
|
directionLock: "x" | "y" | null;
|
||||||
};
|
};
|
||||||
}>;
|
}>;
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user