fix: Arrow with angle jittering

Signed-off-by: Mark Tolmacs <mark@lazycat.hu>
This commit is contained in:
Mark Tolmacs
2025-11-17 19:21:18 +01:00
parent d46c38cb27
commit 66d021a19e
3 changed files with 23 additions and 39 deletions

View File

@@ -1569,7 +1569,9 @@ export class LinearElementEditor {
const updatedOriginPoint =
pointUpdates.get(0)?.point ?? pointFrom<LocalPoint>(0, 0);
const [offsetX, offsetY] = updatedOriginPoint;
// Handle non-normalized points
const offsetX = element.points[0][0] - updatedOriginPoint[0];
const offsetY = element.points[0][1] - updatedOriginPoint[1];
const nextPoints = isElbowArrow(element)
? [
@@ -1586,7 +1588,14 @@ export class LinearElementEditor {
idx !== points.length - 1 &&
!pointUpdates.has(idx)
) {
return pointFrom<LocalPoint>(current[0], current[1]);
return current;
}
// Since we're subtracting the offset coming from the first point
// from all other points, we need to skip the first point here to avoid
// double-subtracting the offset.
if (idx === 0) {
return pointFrom<LocalPoint>(0, 0);
}
return pointFrom<LocalPoint>(

View File

@@ -4,7 +4,7 @@ import throttle from "lodash.throttle";
import { useEffect, useMemo, useState, memo } from "react";
import { STATS_PANELS } from "@excalidraw/common";
import { getCommonBounds, isBindingElement } from "@excalidraw/element";
import { getCommonBounds } from "@excalidraw/element";
import { getUncroppedWidthAndHeight } from "@excalidraw/element";
import { isImageElement } from "@excalidraw/element";
@@ -333,7 +333,6 @@ export const StatsInner = memo(
appState={appState}
/>
</StatsRow>
{!isBindingElement(singleElement) && (
<StatsRow>
<Angle
property="angle"
@@ -342,7 +341,6 @@ export const StatsInner = memo(
appState={appState}
/>
</StatsRow>
)}
<StatsRow>
<FontSize
property="fontSize"

View File

@@ -1,4 +1,4 @@
import { isFiniteNumber, pointFrom, pointRotateRads } from "@excalidraw/math";
import { isFiniteNumber, pointFrom } from "@excalidraw/math";
import {
DEFAULT_FONT_FAMILY,
@@ -20,7 +20,6 @@ import {
} from "@excalidraw/common";
import {
calculateFixedPointForNonElbowArrowBinding,
elementCenterPoint,
getNonDeletedElements,
isPointInElement,
isValidPolygon,
@@ -393,35 +392,14 @@ export const restoreElement = (
...getSizeFromPoints(points),
});
case "arrow": {
const {
startArrowhead = null,
endArrowhead = "arrow",
angle = 0,
} = element;
const { startArrowhead = null, endArrowhead = "arrow" } = element;
const x: number | undefined = element.x;
const y: number | undefined = element.y;
let points: readonly LocalPoint[] | undefined = // migrate old arrow model to new one
const points: readonly LocalPoint[] | undefined = // migrate old arrow model to new one
!Array.isArray(element.points) || element.points.length < 2
? [pointFrom(0, 0), pointFrom(element.width, element.height)]
: element.points;
if (angle !== 0) {
points = LinearElementEditor.getPointsGlobalCoordinates(
element,
elementsMap,
).map((point) =>
LinearElementEditor.pointFromAbsoluteCoords(
element as ExcalidrawArrowElement,
pointRotateRads(
point,
elementCenterPoint(element, elementsMap),
angle,
),
elementsMap,
),
);
}
const base = {
type: element.type,
startBinding: repairBinding(
@@ -443,7 +421,6 @@ export const restoreElement = (
y,
elbowed: (element as ExcalidrawArrowElement).elbowed,
...getSizeFromPoints(points),
angle: 0 as Radians,
};
// TODO: Separate arrow from linear element