simplify app and pointer down state
This commit is contained in:
parent
064bede0c5
commit
06a7a51baa
@ -5106,13 +5106,11 @@ class App extends React.Component<AppProps, AppState> {
|
|||||||
private startImageCropping = (image: ExcalidrawImageElement) => {
|
private startImageCropping = (image: ExcalidrawImageElement) => {
|
||||||
this.setState({
|
this.setState({
|
||||||
croppingElement: image,
|
croppingElement: image,
|
||||||
isCropping: true,
|
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
private finishImageCropping = () => {
|
private finishImageCropping = () => {
|
||||||
this.setState({
|
this.setState({
|
||||||
isCropping: false,
|
|
||||||
croppingElement: null,
|
croppingElement: null,
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
@ -6590,12 +6588,6 @@ class App extends React.Component<AppProps, AppState> {
|
|||||||
arrowDirection: "origin",
|
arrowDirection: "origin",
|
||||||
center: { x: (maxX + minX) / 2, y: (maxY + minY) / 2 },
|
center: { x: (maxX + minX) / 2, y: (maxY + minY) / 2 },
|
||||||
},
|
},
|
||||||
crop: {
|
|
||||||
handleType: false,
|
|
||||||
isCropping: false,
|
|
||||||
offset: { x: 0, y: 0 },
|
|
||||||
complete: false,
|
|
||||||
},
|
|
||||||
hit: {
|
hit: {
|
||||||
element: null,
|
element: null,
|
||||||
allHitElements: [],
|
allHitElements: [],
|
||||||
@ -6716,11 +6708,7 @@ class App extends React.Component<AppProps, AppState> {
|
|||||||
pointerDownState.resize.handleType =
|
pointerDownState.resize.handleType =
|
||||||
elementWithTransformHandleType.transformHandleType;
|
elementWithTransformHandleType.transformHandleType;
|
||||||
} else if (this.state.croppingElement) {
|
} else if (this.state.croppingElement) {
|
||||||
this.setState({
|
pointerDownState.resize.handleType =
|
||||||
croppingElement:
|
|
||||||
elementWithTransformHandleType.element as ExcalidrawImageElement,
|
|
||||||
});
|
|
||||||
pointerDownState.crop.handleType =
|
|
||||||
elementWithTransformHandleType.transformHandleType;
|
elementWithTransformHandleType.transformHandleType;
|
||||||
} else {
|
} else {
|
||||||
this.setState({
|
this.setState({
|
||||||
@ -6761,17 +6749,6 @@ class App extends React.Component<AppProps, AppState> {
|
|||||||
selectedElements[0],
|
selectedElements[0],
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
} else if (pointerDownState.crop.handleType) {
|
|
||||||
pointerDownState.crop.isCropping = true;
|
|
||||||
pointerDownState.crop.offset = tupleToCoors(
|
|
||||||
getResizeOffsetXY(
|
|
||||||
pointerDownState.crop.handleType,
|
|
||||||
selectedElements,
|
|
||||||
this.scene.getNonDeletedElementsMap(),
|
|
||||||
pointerDownState.origin.x,
|
|
||||||
pointerDownState.origin.y,
|
|
||||||
),
|
|
||||||
);
|
|
||||||
} else {
|
} else {
|
||||||
if (this.state.selectedLinearElement) {
|
if (this.state.selectedLinearElement) {
|
||||||
const linearElementEditor =
|
const linearElementEditor =
|
||||||
@ -6806,6 +6783,13 @@ class App extends React.Component<AppProps, AppState> {
|
|||||||
pointerDownState.origin.y,
|
pointerDownState.origin.y,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
if (
|
||||||
|
this.state.croppingElement &&
|
||||||
|
pointerDownState.hit.element !== this.state.croppingElement
|
||||||
|
) {
|
||||||
|
this.finishImageCropping();
|
||||||
|
}
|
||||||
|
|
||||||
if (pointerDownState.hit.element) {
|
if (pointerDownState.hit.element) {
|
||||||
// Early return if pointer is hitting link icon
|
// Early return if pointer is hitting link icon
|
||||||
const hitLinkElement = this.getElementLinkAtPosition(
|
const hitLinkElement = this.getElementLinkAtPosition(
|
||||||
@ -7667,14 +7651,10 @@ class App extends React.Component<AppProps, AppState> {
|
|||||||
if (pointerDownState.resize.isResizing) {
|
if (pointerDownState.resize.isResizing) {
|
||||||
pointerDownState.lastCoords.x = pointerCoords.x;
|
pointerDownState.lastCoords.x = pointerCoords.x;
|
||||||
pointerDownState.lastCoords.y = pointerCoords.y;
|
pointerDownState.lastCoords.y = pointerCoords.y;
|
||||||
if (this.maybeHandleResize(pointerDownState, event)) {
|
if (this.maybeHandleCrop(pointerDownState, event)) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
if (this.maybeHandleResize(pointerDownState, event)) {
|
||||||
if (pointerDownState.crop.isCropping) {
|
|
||||||
pointerDownState.lastCoords.x = pointerCoords.x;
|
|
||||||
pointerDownState.lastCoords.y = pointerCoords.y;
|
|
||||||
if (this.maybeHandleCrop(pointerDownState, event)) {
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -7847,17 +7827,17 @@ class App extends React.Component<AppProps, AppState> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// #region drag
|
// #region move crop region
|
||||||
|
const croppingElement = this.state.croppingElement;
|
||||||
if (
|
if (
|
||||||
selectedElements.length === 1 &&
|
croppingElement &&
|
||||||
isImageElement(selectedElements[0]) &&
|
croppingElement.crop !== null &&
|
||||||
this.state.croppingElement?.id === selectedElements[0].id &&
|
pointerDownState.hit.element === croppingElement
|
||||||
selectedElements[0].crop !== null
|
|
||||||
) {
|
) {
|
||||||
const crop = selectedElements[0].crop;
|
const crop = croppingElement.crop;
|
||||||
const image =
|
const image =
|
||||||
isInitializedImageElement(selectedElements[0]) &&
|
isInitializedImageElement(croppingElement) &&
|
||||||
this.imageCache.get(selectedElements[0].fileId)?.image;
|
this.imageCache.get(croppingElement.fileId)?.image;
|
||||||
|
|
||||||
if (image && !(image instanceof Promise)) {
|
if (image && !(image instanceof Promise)) {
|
||||||
const instantDragOffset = {
|
const instantDragOffset = {
|
||||||
@ -7865,42 +7845,21 @@ class App extends React.Component<AppProps, AppState> {
|
|||||||
y: pointerCoords.y - lastPointerCoords.y,
|
y: pointerCoords.y - lastPointerCoords.y,
|
||||||
};
|
};
|
||||||
|
|
||||||
// current offset is based on the element's width and height
|
|
||||||
const uncroppedWidth =
|
|
||||||
selectedElements[0].initialWidth *
|
|
||||||
selectedElements[0].resizeFactors[0];
|
|
||||||
const uncroppedHeight =
|
|
||||||
selectedElements[0].initialHeight *
|
|
||||||
selectedElements[0].resizeFactors[1];
|
|
||||||
|
|
||||||
const SENSITIVITY_FACTOR = 3;
|
|
||||||
|
|
||||||
const adjustedOffset = {
|
|
||||||
x:
|
|
||||||
instantDragOffset.x *
|
|
||||||
(uncroppedWidth / image.naturalWidth) *
|
|
||||||
SENSITIVITY_FACTOR,
|
|
||||||
y:
|
|
||||||
instantDragOffset.y *
|
|
||||||
(uncroppedHeight / image.naturalHeight) *
|
|
||||||
SENSITIVITY_FACTOR,
|
|
||||||
};
|
|
||||||
|
|
||||||
const nextCrop = {
|
const nextCrop = {
|
||||||
...crop,
|
...crop,
|
||||||
x: clamp(
|
x: clamp(
|
||||||
crop.x - adjustedOffset.x,
|
crop.x - instantDragOffset.x,
|
||||||
0,
|
0,
|
||||||
image.naturalWidth - crop.width,
|
image.naturalWidth - crop.width,
|
||||||
),
|
),
|
||||||
y: clamp(
|
y: clamp(
|
||||||
crop.y - adjustedOffset.y,
|
crop.y - instantDragOffset.y,
|
||||||
0,
|
0,
|
||||||
image.naturalHeight - crop.height,
|
image.naturalHeight - crop.height,
|
||||||
),
|
),
|
||||||
};
|
};
|
||||||
|
|
||||||
mutateElement(selectedElements[0], {
|
mutateElement(croppingElement, {
|
||||||
crop: nextCrop,
|
crop: nextCrop,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
@ -8290,6 +8249,7 @@ class App extends React.Component<AppProps, AppState> {
|
|||||||
const {
|
const {
|
||||||
newElement,
|
newElement,
|
||||||
resizingElement,
|
resizingElement,
|
||||||
|
croppingElement,
|
||||||
multiElement,
|
multiElement,
|
||||||
activeTool,
|
activeTool,
|
||||||
isResizing,
|
isResizing,
|
||||||
@ -8300,6 +8260,7 @@ class App extends React.Component<AppProps, AppState> {
|
|||||||
this.setState((prevState) => ({
|
this.setState((prevState) => ({
|
||||||
isResizing: false,
|
isResizing: false,
|
||||||
isRotating: false,
|
isRotating: false,
|
||||||
|
isCropping: false,
|
||||||
resizingElement: null,
|
resizingElement: null,
|
||||||
selectionElement: null,
|
selectionElement: null,
|
||||||
frameToHighlight: null,
|
frameToHighlight: null,
|
||||||
@ -8793,11 +8754,16 @@ class App extends React.Component<AppProps, AppState> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// click outside the cropping region to exit o0ol
|
||||||
if (
|
if (
|
||||||
isCropping &&
|
// not in the cropping mode at all
|
||||||
!isResizing &&
|
!croppingElement ||
|
||||||
((!hitElement && !pointerDownState.crop.isCropping) ||
|
// in the cropping mode
|
||||||
(hitElement && hitElement !== this.state.croppingElement))
|
(croppingElement &&
|
||||||
|
// not cropping and no hit element
|
||||||
|
((!hitElement && !isCropping) ||
|
||||||
|
// hitting something else
|
||||||
|
(hitElement && hitElement !== croppingElement)))
|
||||||
) {
|
) {
|
||||||
this.finishImageCropping();
|
this.finishImageCropping();
|
||||||
}
|
}
|
||||||
@ -10033,37 +9999,33 @@ class App extends React.Component<AppProps, AppState> {
|
|||||||
pointerDownState: PointerDownState,
|
pointerDownState: PointerDownState,
|
||||||
event: MouseEvent | KeyboardEvent,
|
event: MouseEvent | KeyboardEvent,
|
||||||
): boolean => {
|
): boolean => {
|
||||||
if (pointerDownState.crop.complete) {
|
// to crop, we must already be in the cropping mode, where croppingElement has been set
|
||||||
return true;
|
if (!this.state.croppingElement) {
|
||||||
}
|
|
||||||
const selectedElements = getSelectedElements(
|
|
||||||
this.scene.getNonDeletedElements(),
|
|
||||||
this.state,
|
|
||||||
);
|
|
||||||
|
|
||||||
if (selectedElements.length > 1) {
|
|
||||||
// don't see much sense in allowing multi-crop, that would be weird
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
const transformHandleType = pointerDownState.crop.handleType;
|
const transformHandleType = pointerDownState.resize.handleType;
|
||||||
const pointerCoords = pointerDownState.lastCoords;
|
const pointerCoords = pointerDownState.lastCoords;
|
||||||
const [x, y] = getGridPoint(
|
const [x, y] = getGridPoint(
|
||||||
pointerCoords.x - pointerDownState.crop.offset.x,
|
pointerCoords.x - pointerDownState.resize.offset.x,
|
||||||
pointerCoords.y - pointerDownState.crop.offset.y,
|
pointerCoords.y - pointerDownState.resize.offset.y,
|
||||||
this.getEffectiveGridSize(),
|
this.getEffectiveGridSize(),
|
||||||
);
|
);
|
||||||
|
|
||||||
const elementToCrop = selectedElements[0] as ExcalidrawImageElement;
|
|
||||||
if (transformHandleType) {
|
if (transformHandleType) {
|
||||||
cropElement(
|
cropElement(
|
||||||
elementToCrop,
|
this.state.croppingElement,
|
||||||
this.scene.getNonDeletedElementsMap(),
|
this.scene.getNonDeletedElementsMap(),
|
||||||
this.imageCache,
|
this.imageCache,
|
||||||
transformHandleType,
|
transformHandleType,
|
||||||
x,
|
x,
|
||||||
y,
|
y,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
this.setState({
|
||||||
|
isCropping: transformHandleType && transformHandleType !== "rotation",
|
||||||
|
});
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -107,7 +107,6 @@ const getRelevantAppStateProps = (
|
|||||||
frameToHighlight: appState.frameToHighlight,
|
frameToHighlight: appState.frameToHighlight,
|
||||||
editingGroupId: appState.editingGroupId,
|
editingGroupId: appState.editingGroupId,
|
||||||
currentHoveredFontFamily: appState.currentHoveredFontFamily,
|
currentHoveredFontFamily: appState.currentHoveredFontFamily,
|
||||||
isCropping: appState.isCropping,
|
|
||||||
croppingElement: appState.croppingElement,
|
croppingElement: appState.croppingElement,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import { Point } from "points-on-curve";
|
import { type Point } from "points-on-curve";
|
||||||
import {
|
import {
|
||||||
type Radians,
|
type Radians,
|
||||||
point,
|
point,
|
||||||
@ -15,7 +15,7 @@ import {
|
|||||||
import { updateBoundElements } from "./binding";
|
import { updateBoundElements } from "./binding";
|
||||||
import { mutateElement } from "./mutateElement";
|
import { mutateElement } from "./mutateElement";
|
||||||
import { TransformHandleType } from "./transformHandles";
|
import { TransformHandleType } from "./transformHandles";
|
||||||
import {
|
import type {
|
||||||
ElementsMap,
|
ElementsMap,
|
||||||
ExcalidrawElement,
|
ExcalidrawElement,
|
||||||
ExcalidrawImageElement,
|
ExcalidrawImageElement,
|
||||||
|
@ -602,7 +602,7 @@ const renderCropHandles = (
|
|||||||
const lineWidth = 3 / appState.zoom.value;
|
const lineWidth = 3 / appState.zoom.value;
|
||||||
const length = 15 / appState.zoom.value;
|
const length = 15 / appState.zoom.value;
|
||||||
|
|
||||||
const [x1, y1, x2, y2, cx, cy] = getElementAbsoluteCoords(
|
const [x1, y1, , , cx, cy] = getElementAbsoluteCoords(
|
||||||
croppingElement,
|
croppingElement,
|
||||||
elementsMap,
|
elementsMap,
|
||||||
);
|
);
|
||||||
@ -1002,7 +1002,7 @@ const _renderInteractiveScene = ({
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (appState.isCropping && appState.croppingElement) {
|
if (appState.croppingElement && !appState.isCropping) {
|
||||||
renderCropHandles(
|
renderCropHandles(
|
||||||
context,
|
context,
|
||||||
renderConfig,
|
renderConfig,
|
||||||
|
@ -177,7 +177,6 @@ export type StaticCanvasAppState = Readonly<
|
|||||||
frameRendering: AppState["frameRendering"];
|
frameRendering: AppState["frameRendering"];
|
||||||
currentHoveredFontFamily: AppState["currentHoveredFontFamily"];
|
currentHoveredFontFamily: AppState["currentHoveredFontFamily"];
|
||||||
// Cropping
|
// Cropping
|
||||||
isCropping: AppState["isCropping"];
|
|
||||||
croppingElement: AppState["croppingElement"];
|
croppingElement: AppState["croppingElement"];
|
||||||
}
|
}
|
||||||
>;
|
>;
|
||||||
@ -677,12 +676,6 @@ export type PointerDownState = Readonly<{
|
|||||||
// This is a center point of selected elements determined on the initial pointer down event (for rotation only)
|
// This is a center point of selected elements determined on the initial pointer down event (for rotation only)
|
||||||
center: { x: number; y: number };
|
center: { x: number; y: number };
|
||||||
};
|
};
|
||||||
crop: {
|
|
||||||
handleType: MaybeTransformHandleType;
|
|
||||||
isCropping: boolean;
|
|
||||||
offset: { x: number; y: number };
|
|
||||||
complete: boolean;
|
|
||||||
};
|
|
||||||
hit: {
|
hit: {
|
||||||
// The element the pointer is "hitting", is determined on the initial
|
// The element the pointer is "hitting", is determined on the initial
|
||||||
// pointer down event
|
// pointer down event
|
||||||
|
Loading…
x
Reference in New Issue
Block a user