|
|
@ -5,6 +5,13 @@ import { getStepSizedValue, isPropertyEditable, resizeElement } from "./utils";
|
|
|
|
import { MIN_WIDTH_OR_HEIGHT } from "../../constants";
|
|
|
|
import { MIN_WIDTH_OR_HEIGHT } from "../../constants";
|
|
|
|
import type Scene from "../../scene/Scene";
|
|
|
|
import type Scene from "../../scene/Scene";
|
|
|
|
import type { AppState } from "../../types";
|
|
|
|
import type { AppState } from "../../types";
|
|
|
|
|
|
|
|
import { isImageElement } from "../../element/typeChecks";
|
|
|
|
|
|
|
|
import {
|
|
|
|
|
|
|
|
MINIMAL_CROP_SIZE,
|
|
|
|
|
|
|
|
getUncroppedWidthAndHeight,
|
|
|
|
|
|
|
|
} from "../../element/cropElement";
|
|
|
|
|
|
|
|
import { mutateElement } from "../../element/mutateElement";
|
|
|
|
|
|
|
|
import { clamp, round } from "../../../math";
|
|
|
|
|
|
|
|
|
|
|
|
interface DimensionDragInputProps {
|
|
|
|
interface DimensionDragInputProps {
|
|
|
|
property: "width" | "height";
|
|
|
|
property: "width" | "height";
|
|
|
@ -27,6 +34,8 @@ const handleDimensionChange: DragInputCallbackType<
|
|
|
|
shouldChangeByStepSize,
|
|
|
|
shouldChangeByStepSize,
|
|
|
|
nextValue,
|
|
|
|
nextValue,
|
|
|
|
property,
|
|
|
|
property,
|
|
|
|
|
|
|
|
originalAppState,
|
|
|
|
|
|
|
|
instantChange,
|
|
|
|
scene,
|
|
|
|
scene,
|
|
|
|
}) => {
|
|
|
|
}) => {
|
|
|
|
const elementsMap = scene.getNonDeletedElementsMap();
|
|
|
|
const elementsMap = scene.getNonDeletedElementsMap();
|
|
|
@ -37,6 +46,107 @@ const handleDimensionChange: DragInputCallbackType<
|
|
|
|
shouldKeepAspectRatio || _shouldKeepAspectRatio(origElement);
|
|
|
|
shouldKeepAspectRatio || _shouldKeepAspectRatio(origElement);
|
|
|
|
const aspectRatio = origElement.width / origElement.height;
|
|
|
|
const aspectRatio = origElement.width / origElement.height;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (originalAppState.croppingElementId === origElement.id) {
|
|
|
|
|
|
|
|
const element = elementsMap.get(origElement.id);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (!element || !isImageElement(element) || !element.crop) {
|
|
|
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const crop = element.crop;
|
|
|
|
|
|
|
|
let nextCrop = { ...crop };
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const isFlippedByX = element.scale[0] === -1;
|
|
|
|
|
|
|
|
const isFlippedByY = element.scale[1] === -1;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const { width: uncroppedWidth, height: uncroppedHeight } =
|
|
|
|
|
|
|
|
getUncroppedWidthAndHeight(element);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const naturalToUncroppedWidthRatio = crop.naturalWidth / uncroppedWidth;
|
|
|
|
|
|
|
|
const naturalToUncroppedHeightRatio =
|
|
|
|
|
|
|
|
crop.naturalHeight / uncroppedHeight;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const MAX_POSSIBLE_WIDTH = isFlippedByX
|
|
|
|
|
|
|
|
? crop.width + crop.x
|
|
|
|
|
|
|
|
: crop.naturalWidth - crop.x;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const MAX_POSSIBLE_HEIGHT = isFlippedByY
|
|
|
|
|
|
|
|
? crop.height + crop.y
|
|
|
|
|
|
|
|
: crop.naturalHeight - crop.y;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const MIN_WIDTH = MINIMAL_CROP_SIZE * naturalToUncroppedWidthRatio;
|
|
|
|
|
|
|
|
const MIN_HEIGHT = MINIMAL_CROP_SIZE * naturalToUncroppedHeightRatio;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (nextValue !== undefined) {
|
|
|
|
|
|
|
|
if (property === "width") {
|
|
|
|
|
|
|
|
const nextValueInNatural = nextValue * naturalToUncroppedWidthRatio;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const nextCropWidth = clamp(
|
|
|
|
|
|
|
|
nextValueInNatural,
|
|
|
|
|
|
|
|
MIN_WIDTH,
|
|
|
|
|
|
|
|
MAX_POSSIBLE_WIDTH,
|
|
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
nextCrop = {
|
|
|
|
|
|
|
|
...nextCrop,
|
|
|
|
|
|
|
|
width: nextCropWidth,
|
|
|
|
|
|
|
|
x: isFlippedByX ? crop.x + crop.width - nextCropWidth : crop.x,
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
} else if (property === "height") {
|
|
|
|
|
|
|
|
const nextValueInNatural = nextValue * naturalToUncroppedHeightRatio;
|
|
|
|
|
|
|
|
const nextCropHeight = clamp(
|
|
|
|
|
|
|
|
nextValueInNatural,
|
|
|
|
|
|
|
|
MIN_HEIGHT,
|
|
|
|
|
|
|
|
MAX_POSSIBLE_HEIGHT,
|
|
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
nextCrop = {
|
|
|
|
|
|
|
|
...nextCrop,
|
|
|
|
|
|
|
|
height: nextCropHeight,
|
|
|
|
|
|
|
|
y: isFlippedByY ? crop.y + crop.height - nextCropHeight : crop.y,
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
mutateElement(element, {
|
|
|
|
|
|
|
|
crop: nextCrop,
|
|
|
|
|
|
|
|
width: nextCrop.width / (crop.naturalWidth / uncroppedWidth),
|
|
|
|
|
|
|
|
height: nextCrop.height / (crop.naturalHeight / uncroppedHeight),
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const changeInWidth = property === "width" ? instantChange : 0;
|
|
|
|
|
|
|
|
const changeInHeight = property === "height" ? instantChange : 0;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const nextCropWidth = clamp(
|
|
|
|
|
|
|
|
crop.width + changeInWidth,
|
|
|
|
|
|
|
|
MIN_WIDTH,
|
|
|
|
|
|
|
|
MAX_POSSIBLE_WIDTH,
|
|
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const nextCropHeight = clamp(
|
|
|
|
|
|
|
|
crop.height + changeInHeight,
|
|
|
|
|
|
|
|
MIN_WIDTH,
|
|
|
|
|
|
|
|
MAX_POSSIBLE_HEIGHT,
|
|
|
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
nextCrop = {
|
|
|
|
|
|
|
|
...crop,
|
|
|
|
|
|
|
|
x: isFlippedByX ? crop.x + crop.width - nextCropWidth : crop.x,
|
|
|
|
|
|
|
|
y: isFlippedByY ? crop.y + crop.height - nextCropHeight : crop.y,
|
|
|
|
|
|
|
|
width: nextCropWidth,
|
|
|
|
|
|
|
|
height: nextCropHeight,
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
mutateElement(element, {
|
|
|
|
|
|
|
|
crop: nextCrop,
|
|
|
|
|
|
|
|
width: nextCrop.width / (crop.naturalWidth / uncroppedWidth),
|
|
|
|
|
|
|
|
height: nextCrop.height / (crop.naturalHeight / uncroppedHeight),
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (nextValue !== undefined) {
|
|
|
|
if (nextValue !== undefined) {
|
|
|
|
const nextWidth = Math.max(
|
|
|
|
const nextWidth = Math.max(
|
|
|
|
property === "width"
|
|
|
|
property === "width"
|
|
|
@ -117,9 +227,25 @@ const DimensionDragInput = ({
|
|
|
|
scene,
|
|
|
|
scene,
|
|
|
|
appState,
|
|
|
|
appState,
|
|
|
|
}: DimensionDragInputProps) => {
|
|
|
|
}: DimensionDragInputProps) => {
|
|
|
|
const value =
|
|
|
|
let value = round(property === "width" ? element.width : element.height, 2);
|
|
|
|
Math.round((property === "width" ? element.width : element.height) * 100) /
|
|
|
|
|
|
|
|
100;
|
|
|
|
if (
|
|
|
|
|
|
|
|
appState.croppingElementId &&
|
|
|
|
|
|
|
|
appState.croppingElementId === element.id &&
|
|
|
|
|
|
|
|
isImageElement(element) &&
|
|
|
|
|
|
|
|
element.crop
|
|
|
|
|
|
|
|
) {
|
|
|
|
|
|
|
|
const { width: uncroppedWidth, height: uncroppedHeight } =
|
|
|
|
|
|
|
|
getUncroppedWidthAndHeight(element);
|
|
|
|
|
|
|
|
if (property === "width") {
|
|
|
|
|
|
|
|
const ratio = uncroppedWidth / element.crop.naturalWidth;
|
|
|
|
|
|
|
|
value = round(element.crop.width * ratio, 2);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
if (property === "height") {
|
|
|
|
|
|
|
|
const ratio = uncroppedHeight / element.crop.naturalHeight;
|
|
|
|
|
|
|
|
value = round(element.crop.height * ratio, 2);
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return (
|
|
|
|
return (
|
|
|
|
<DragInput
|
|
|
|
<DragInput
|
|
|
|