mirror of
https://github.com/excalidraw/excalidraw.git
synced 2024-11-10 11:35:52 +01:00
Fix multielements (#987)
This commit is contained in:
parent
0dc07135b7
commit
b603337c3f
@ -99,6 +99,7 @@ import { mutateElement, newElementWith } from "../element/mutateElement";
|
||||
import { invalidateShapeForElement } from "../renderer/renderElement";
|
||||
import { unstable_batchedUpdates } from "react-dom";
|
||||
import { SceneStateCallbackRemover } from "../scene/globalScene";
|
||||
import { rescalePoints } from "../points";
|
||||
|
||||
function withBatchedUpdates<
|
||||
TFunction extends ((event: any) => void) | (() => void)
|
||||
@ -1801,89 +1802,68 @@ export class App extends React.Component<any, AppState> {
|
||||
}
|
||||
break;
|
||||
case "n": {
|
||||
let points;
|
||||
if (element.points.length > 0) {
|
||||
const len = element.points.length;
|
||||
points = [...element.points].sort((a, b) => a[1] - b[1]) as [
|
||||
number,
|
||||
number,
|
||||
][];
|
||||
|
||||
for (let i = 1; i < points.length; ++i) {
|
||||
const pnt = points[i];
|
||||
pnt[1] -= deltaY / (len - i);
|
||||
}
|
||||
const height = element.height - deltaY;
|
||||
if (height <= 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
mutateElement(element, {
|
||||
height: element.height - deltaY,
|
||||
height,
|
||||
y: element.y + deltaY,
|
||||
points,
|
||||
points:
|
||||
element.points.length > 0
|
||||
? rescalePoints(1, height, element.points)
|
||||
: undefined,
|
||||
});
|
||||
|
||||
break;
|
||||
}
|
||||
case "w": {
|
||||
let points;
|
||||
if (element.points.length > 0) {
|
||||
const len = element.points.length;
|
||||
points = [...element.points].sort((a, b) => a[0] - b[0]) as [
|
||||
number,
|
||||
number,
|
||||
][];
|
||||
const width = element.width - deltaX;
|
||||
|
||||
for (let i = 0; i < points.length; ++i) {
|
||||
const pnt = points[i];
|
||||
pnt[0] -= deltaX / (len - i);
|
||||
}
|
||||
if (width <= 0) {
|
||||
// Someday we should implement logic to flip the shape. But for now, just stop.
|
||||
break;
|
||||
}
|
||||
|
||||
mutateElement(element, {
|
||||
width: element.width - deltaX,
|
||||
width,
|
||||
x: element.x + deltaX,
|
||||
points,
|
||||
points:
|
||||
element.points.length > 0
|
||||
? rescalePoints(0, width, element.points)
|
||||
: undefined,
|
||||
});
|
||||
break;
|
||||
}
|
||||
case "s": {
|
||||
let points;
|
||||
|
||||
if (element.points.length > 0) {
|
||||
const len = element.points.length;
|
||||
points = [...element.points].sort((a, b) => a[1] - b[1]) as [
|
||||
number,
|
||||
number,
|
||||
][];
|
||||
|
||||
for (let i = 1; i < points.length; ++i) {
|
||||
const pnt = points[i];
|
||||
pnt[1] += deltaY / (len - i);
|
||||
}
|
||||
const height = element.height + deltaY;
|
||||
if (height <= 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
mutateElement(element, {
|
||||
height: element.height + deltaY,
|
||||
points,
|
||||
height,
|
||||
points:
|
||||
element.points.length > 0
|
||||
? rescalePoints(1, height, element.points)
|
||||
: undefined,
|
||||
});
|
||||
|
||||
break;
|
||||
}
|
||||
case "e": {
|
||||
let points;
|
||||
if (element.points.length > 0) {
|
||||
const len = element.points.length;
|
||||
points = [...element.points].sort((a, b) => a[0] - b[0]) as [
|
||||
number,
|
||||
number,
|
||||
][];
|
||||
|
||||
for (let i = 1; i < points.length; ++i) {
|
||||
const pnt = points[i];
|
||||
pnt[0] += deltaX / (len - i);
|
||||
}
|
||||
const width = element.width + deltaX;
|
||||
if (width <= 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
mutateElement(element, {
|
||||
width: element.width + deltaX,
|
||||
points,
|
||||
width,
|
||||
points:
|
||||
element.points.length > 0
|
||||
? rescalePoints(0, width, element.points)
|
||||
: undefined,
|
||||
});
|
||||
break;
|
||||
}
|
||||
|
@ -2,6 +2,7 @@ import { ExcalidrawElement } from "./types";
|
||||
import { randomSeed } from "roughjs/bin/math";
|
||||
import { invalidateShapeForElement } from "../renderer/renderElement";
|
||||
import { globalSceneState } from "../scene";
|
||||
import { getSizeFromPoints } from "../points";
|
||||
|
||||
type ElementUpdate<TElement extends ExcalidrawElement> = Omit<
|
||||
Partial<TElement>,
|
||||
@ -18,6 +19,10 @@ export function mutateElement<TElement extends ExcalidrawElement>(
|
||||
) {
|
||||
const mutableElement = element as any;
|
||||
|
||||
if (typeof updates.points !== "undefined") {
|
||||
updates = { ...getSizeFromPoints(updates.points!), ...updates };
|
||||
}
|
||||
|
||||
for (const key in updates) {
|
||||
const value = (updates as any)[key];
|
||||
if (typeof value !== "undefined") {
|
||||
|
46
src/points.ts
Normal file
46
src/points.ts
Normal file
@ -0,0 +1,46 @@
|
||||
import { Point } from "./types";
|
||||
|
||||
export function getSizeFromPoints(points: readonly Point[]) {
|
||||
const xs = points.map(point => point[0]);
|
||||
const ys = points.map(point => point[1]);
|
||||
return {
|
||||
width: Math.max(...xs) - Math.min(...xs),
|
||||
height: Math.max(...ys) - Math.min(...ys),
|
||||
};
|
||||
}
|
||||
export function rescalePoints(
|
||||
dimension: 0 | 1,
|
||||
nextDimensionSize: number,
|
||||
prevPoints: readonly Point[],
|
||||
): readonly Point[] {
|
||||
const prevDimValues = prevPoints.map(point => point[dimension]);
|
||||
const prevMaxDimension = Math.max(...prevDimValues);
|
||||
const prevMinDimension = Math.min(...prevDimValues);
|
||||
const prevDimensionSize = prevMaxDimension - prevMinDimension;
|
||||
|
||||
const dimensionScaleFactor = nextDimensionSize / prevDimensionSize;
|
||||
|
||||
let nextMinDimension = Infinity;
|
||||
|
||||
const scaledPoints = prevPoints.map(prevPoint =>
|
||||
prevPoint.map((value, currentDimension) => {
|
||||
if (currentDimension !== dimension) {
|
||||
return value;
|
||||
}
|
||||
const scaledValue = value * dimensionScaleFactor;
|
||||
nextMinDimension = Math.min(scaledValue, nextMinDimension);
|
||||
return scaledValue;
|
||||
}),
|
||||
);
|
||||
|
||||
const translation = prevMinDimension - nextMinDimension;
|
||||
|
||||
const nextPoints = scaledPoints.map(
|
||||
scaledPoint =>
|
||||
scaledPoint.map((value, currentDimension) => {
|
||||
return currentDimension === dimension ? value + translation : value;
|
||||
}) as [number, number],
|
||||
);
|
||||
|
||||
return nextPoints;
|
||||
}
|
Loading…
Reference in New Issue
Block a user