fix: normalize linear element points on restore (#3633)

This commit is contained in:
David Luzar 2021-05-24 20:35:53 +02:00 committed by GitHub
parent d201d0be1b
commit 0bbb4535cf
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
2 changed files with 41 additions and 22 deletions

View File

@ -15,6 +15,7 @@ import {
DEFAULT_VERTICAL_ALIGN, DEFAULT_VERTICAL_ALIGN,
} from "../constants"; } from "../constants";
import { getDefaultAppState } from "../appState"; import { getDefaultAppState } from "../appState";
import { LinearElementEditor } from "../element/linearElementEditor";
type RestoredAppState = Omit< type RestoredAppState = Omit<
AppState, AppState,
@ -49,14 +50,18 @@ const getFontFamilyByName = (fontFamilyName: string): FontFamily => {
return DEFAULT_FONT_FAMILY; return DEFAULT_FONT_FAMILY;
}; };
const restoreElementWithProperties = <T extends ExcalidrawElement>( const restoreElementWithProperties = <
T extends ExcalidrawElement,
K extends keyof Omit<
Required<T>,
Exclude<keyof ExcalidrawElement, "type" | "x" | "y">
>
>(
element: Required<T>, element: Required<T>,
extra: Omit<Required<T>, keyof ExcalidrawElement> & { extra: Pick<T, K>,
type?: ExcalidrawElement["type"];
},
): T => { ): T => {
const base: Pick<T, keyof ExcalidrawElement> = { const base: Pick<T, keyof ExcalidrawElement> = {
type: extra.type || element.type, type: (extra as Partial<T>).type || element.type,
// all elements must have version > 0 so getSceneVersion() will pick up // all elements must have version > 0 so getSceneVersion() will pick up
// newly added elements // newly added elements
version: element.version || 1, version: element.version || 1,
@ -69,8 +74,8 @@ const restoreElementWithProperties = <T extends ExcalidrawElement>(
roughness: element.roughness ?? 1, roughness: element.roughness ?? 1,
opacity: element.opacity == null ? 100 : element.opacity, opacity: element.opacity == null ? 100 : element.opacity,
angle: element.angle || 0, angle: element.angle || 0,
x: element.x || 0, x: (extra as Partial<T>).x ?? element.x ?? 0,
y: element.y || 0, y: (extra as Partial<T>).y ?? element.y ?? 0,
strokeColor: element.strokeColor, strokeColor: element.strokeColor,
backgroundColor: element.backgroundColor, backgroundColor: element.backgroundColor,
width: element.width || 0, width: element.width || 0,
@ -131,6 +136,20 @@ const restoreElement = (
endArrowhead = element.type === "arrow" ? "arrow" : null, endArrowhead = element.type === "arrow" ? "arrow" : null,
} = element; } = element;
let x = element.x;
let y = element.y;
let points = // migrate old arrow model to new one
!Array.isArray(element.points) || element.points.length < 2
? [
[0, 0],
[element.width, element.height],
]
: element.points;
if (points[0][0] !== 0 || points[0][1] !== 0) {
({ points, x, y } = LinearElementEditor.getNormalizedPoints(element));
}
return restoreElementWithProperties(element, { return restoreElementWithProperties(element, {
type: type:
(element.type as ExcalidrawElement["type"] | "draw") === "draw" (element.type as ExcalidrawElement["type"] | "draw") === "draw"
@ -138,17 +157,12 @@ const restoreElement = (
: element.type, : element.type,
startBinding: element.startBinding, startBinding: element.startBinding,
endBinding: element.endBinding, endBinding: element.endBinding,
points:
// migrate old arrow model to new one
!Array.isArray(element.points) || element.points.length < 2
? [
[0, 0],
[element.width, element.height],
]
: element.points,
lastCommittedPoint: null, lastCommittedPoint: null,
startArrowhead, startArrowhead,
endArrowhead, endArrowhead,
points,
x,
y,
}); });
} }
// generic elements // generic elements

View File

@ -415,26 +415,31 @@ export class LinearElementEditor {
return [rotatedX - element.x, rotatedY - element.y]; return [rotatedX - element.x, rotatedY - element.y];
} }
// element-mutating methods
// ---------------------------------------------------------------------------
/** /**
* Normalizes line points so that the start point is at [0,0]. This is * Normalizes line points so that the start point is at [0,0]. This is
* expected in various parts of the codebase. * expected in various parts of the codebase. Also returns new x/y to account
* for the potential normalization.
*/ */
static normalizePoints(element: NonDeleted<ExcalidrawLinearElement>) { static getNormalizedPoints(element: ExcalidrawLinearElement) {
const { points } = element; const { points } = element;
const offsetX = points[0][0]; const offsetX = points[0][0];
const offsetY = points[0][1]; const offsetY = points[0][1];
mutateElement(element, { return {
points: points.map((point, _idx) => { points: points.map((point, _idx) => {
return [point[0] - offsetX, point[1] - offsetY] as const; return [point[0] - offsetX, point[1] - offsetY] as const;
}), }),
x: element.x + offsetX, x: element.x + offsetX,
y: element.y + offsetY, y: element.y + offsetY,
}); };
}
// element-mutating methods
// ---------------------------------------------------------------------------
static normalizePoints(element: NonDeleted<ExcalidrawLinearElement>) {
mutateElement(element, LinearElementEditor.getNormalizedPoints(element));
} }
static movePointByOffset( static movePointByOffset(