diff --git a/packages/common/src/constants.ts b/packages/common/src/constants.ts
index c3c348cebc..c797c6e8c2 100644
--- a/packages/common/src/constants.ts
+++ b/packages/common/src/constants.ts
@@ -36,6 +36,7 @@ export const APP_NAME = "Excalidraw";
// (happens a lot with fast clicks with the text tool)
export const TEXT_AUTOWRAP_THRESHOLD = 36; // px
export const DRAGGING_THRESHOLD = 10; // px
+export const MINIMUM_ARROW_SIZE = 20; // px
export const LINE_CONFIRM_THRESHOLD = 8; // px
export const ELEMENT_SHIFT_TRANSLATE_AMOUNT = 5;
export const ELEMENT_TRANSLATE_AMOUNT = 1;
diff --git a/packages/element/src/renderElement.ts b/packages/element/src/renderElement.ts
index e870d977fb..008d6afc4a 100644
--- a/packages/element/src/renderElement.ts
+++ b/packages/element/src/renderElement.ts
@@ -106,6 +106,11 @@ const getCanvasPadding = (element: ExcalidrawElement) => {
return element.strokeWidth * 12;
case "text":
return element.fontSize / 2;
+ case "arrow":
+ if (element.endArrowhead || element.endArrowhead) {
+ return 40;
+ }
+ return 20;
default:
return 20;
}
diff --git a/packages/excalidraw/actions/actionFinalize.tsx b/packages/excalidraw/actions/actionFinalize.tsx
index 7a4511e051..b699e9ea64 100644
--- a/packages/excalidraw/actions/actionFinalize.tsx
+++ b/packages/excalidraw/actions/actionFinalize.tsx
@@ -154,11 +154,7 @@ export const actionFinalize = register({
if (element) {
// pen and mouse have hover
- if (
- appState.multiElement &&
- element.type !== "freedraw" &&
- appState.lastPointerDownWith !== "touch"
- ) {
+ if (appState.multiElement && element.type !== "freedraw") {
const { points, lastCommittedPoint } = element;
if (
!lastCommittedPoint ||
diff --git a/packages/excalidraw/components/Actions.tsx b/packages/excalidraw/components/Actions.tsx
index 919e9c688d..1b42e9f402 100644
--- a/packages/excalidraw/components/Actions.tsx
+++ b/packages/excalidraw/components/Actions.tsx
@@ -505,15 +505,3 @@ export const ExitZenModeAction = ({
{t("buttons.exitZenMode")}
);
-
-export const FinalizeAction = ({
- renderAction,
- className,
-}: {
- renderAction: ActionManager["renderAction"];
- className?: string;
-}) => (
-
- {renderAction("finalize", { size: "small" })}
-
-);
diff --git a/packages/excalidraw/components/App.tsx b/packages/excalidraw/components/App.tsx
index 1397a8b7c2..dcb9f9838f 100644
--- a/packages/excalidraw/components/App.tsx
+++ b/packages/excalidraw/components/App.tsx
@@ -100,6 +100,7 @@ import {
randomInteger,
CLASSES,
Emitter,
+ MINIMUM_ARROW_SIZE,
} from "@excalidraw/common";
import {
@@ -8162,7 +8163,9 @@ class App extends React.Component {
pointDistance(
pointFrom(pointerCoords.x, pointerCoords.y),
pointFrom(pointerDownState.origin.x, pointerDownState.origin.y),
- ) < DRAGGING_THRESHOLD
+ ) *
+ this.state.zoom.value <
+ MINIMUM_ARROW_SIZE
) {
return;
}
@@ -9113,25 +9116,54 @@ class App extends React.Component {
this.state,
);
- if (!pointerDownState.drag.hasOccurred && newElement && !multiElement) {
- this.scene.mutateElement(
- newElement,
- {
- points: [
- ...newElement.points,
- pointFrom(
- pointerCoords.x - newElement.x,
- pointerCoords.y - newElement.y,
- ),
- ],
- },
- { informMutation: false, isDragging: false },
- );
+ const dragDistance =
+ pointDistance(
+ pointFrom(pointerCoords.x, pointerCoords.y),
+ pointFrom(pointerDownState.origin.x, pointerDownState.origin.y),
+ ) * this.state.zoom.value;
- this.setState({
- multiElement: newElement,
- newElement,
- });
+ if (
+ (!pointerDownState.drag.hasOccurred ||
+ dragDistance < MINIMUM_ARROW_SIZE) &&
+ newElement &&
+ !multiElement
+ ) {
+ if (this.device.isTouchScreen) {
+ const FIXED_DELTA_X = Math.min(
+ (this.state.width * 0.7) / this.state.zoom.value,
+ 100,
+ );
+
+ this.scene.mutateElement(
+ newElement,
+ {
+ x: newElement.x - FIXED_DELTA_X / 2,
+ points: [
+ pointFrom(0, 0),
+ pointFrom(FIXED_DELTA_X, 0),
+ ],
+ },
+ { informMutation: false, isDragging: false },
+ );
+
+ this.actionManager.executeAction(actionFinalize);
+ } else {
+ const dx = pointerCoords.x - newElement.x;
+ const dy = pointerCoords.y - newElement.y;
+
+ this.scene.mutateElement(
+ newElement,
+ {
+ points: [...newElement.points, pointFrom(dx, dy)],
+ },
+ { informMutation: false, isDragging: false },
+ );
+
+ this.setState({
+ multiElement: newElement,
+ newElement,
+ });
+ }
} else if (pointerDownState.drag.hasOccurred && !multiElement) {
if (
isBindingEnabled(this.state) &&
diff --git a/packages/excalidraw/components/footer/Footer.tsx b/packages/excalidraw/components/footer/Footer.tsx
index 427628e7c9..3b213d796a 100644
--- a/packages/excalidraw/components/footer/Footer.tsx
+++ b/packages/excalidraw/components/footer/Footer.tsx
@@ -2,13 +2,7 @@ import clsx from "clsx";
import { actionShortcuts } from "../../actions";
import { useTunnels } from "../../context/tunnels";
-import {
- ExitZenModeAction,
- FinalizeAction,
- UndoRedoActions,
- ZoomActions,
-} from "../Actions";
-import { useDevice } from "../App";
+import { ExitZenModeAction, UndoRedoActions, ZoomActions } from "../Actions";
import { HelpButton } from "../HelpButton";
import { Section } from "../Section";
import Stack from "../Stack";
@@ -29,10 +23,6 @@ const Footer = ({
}) => {
const { FooterCenterTunnel, WelcomeScreenHelpHintTunnel } = useTunnels();
- const device = useDevice();
- const showFinalize =
- !appState.viewModeEnabled && appState.multiElement && device.isTouchScreen;
-
return (
)}
- {showFinalize && (
-
- )}
diff --git a/packages/excalidraw/renderer/renderNewElementScene.ts b/packages/excalidraw/renderer/renderNewElementScene.ts
index 69966687d1..dea62bfc0a 100644
--- a/packages/excalidraw/renderer/renderNewElementScene.ts
+++ b/packages/excalidraw/renderer/renderNewElementScene.ts
@@ -1,6 +1,6 @@
import { throttleRAF } from "@excalidraw/common";
-import { renderElement } from "@excalidraw/element";
+import { isInvisiblySmallElement, renderElement } from "@excalidraw/element";
import { bootstrapCanvas, getNormalizedCanvasDimensions } from "./helpers";
@@ -34,6 +34,14 @@ const _renderNewElementScene = ({
context.scale(appState.zoom.value, appState.zoom.value);
if (newElement && newElement.type !== "selection") {
+ // e.g. when creating arrows and we're still below the arrow drag distance
+ // threshold
+ // (for now we skip render only with elements while we're creating to be
+ // safe)
+ if (isInvisiblySmallElement(newElement)) {
+ return;
+ }
+
renderElement(
newElement,
elementsMap,
diff --git a/packages/excalidraw/tests/__snapshots__/regressionTests.test.tsx.snap b/packages/excalidraw/tests/__snapshots__/regressionTests.test.tsx.snap
index c5ac974eca..56b9c40fbb 100644
--- a/packages/excalidraw/tests/__snapshots__/regressionTests.test.tsx.snap
+++ b/packages/excalidraw/tests/__snapshots__/regressionTests.test.tsx.snap
@@ -8190,7 +8190,7 @@ exports[`regression tests > key 2 selects rectangle tool > [end of test] undo st
"fillStyle": "solid",
"frameId": null,
"groupIds": [],
- "height": 10,
+ "height": 30,
"index": "a0",
"isDeleted": false,
"link": null,
@@ -8203,7 +8203,7 @@ exports[`regression tests > key 2 selects rectangle tool > [end of test] undo st
"strokeWidth": 2,
"type": "rectangle",
"version": 3,
- "width": 10,
+ "width": 30,
"x": 10,
"y": 10,
},
@@ -8369,7 +8369,7 @@ exports[`regression tests > key 3 selects diamond tool > [end of test] undo stac
"fillStyle": "solid",
"frameId": null,
"groupIds": [],
- "height": 10,
+ "height": 30,
"index": "a0",
"isDeleted": false,
"link": null,
@@ -8382,7 +8382,7 @@ exports[`regression tests > key 3 selects diamond tool > [end of test] undo stac
"strokeWidth": 2,
"type": "diamond",
"version": 3,
- "width": 10,
+ "width": 30,
"x": 10,
"y": 10,
},
@@ -8548,7 +8548,7 @@ exports[`regression tests > key 4 selects ellipse tool > [end of test] undo stac
"fillStyle": "solid",
"frameId": null,
"groupIds": [],
- "height": 10,
+ "height": 30,
"index": "a0",
"isDeleted": false,
"link": null,
@@ -8561,7 +8561,7 @@ exports[`regression tests > key 4 selects ellipse tool > [end of test] undo stac
"strokeWidth": 2,
"type": "ellipse",
"version": 3,
- "width": 10,
+ "width": 30,
"x": 10,
"y": 10,
},
@@ -8758,7 +8758,7 @@ exports[`regression tests > key 5 selects arrow tool > [end of test] undo stack
"fillStyle": "solid",
"frameId": null,
"groupIds": [],
- "height": 10,
+ "height": 30,
"index": "a0",
"isDeleted": false,
"lastCommittedPoint": null,
@@ -8771,8 +8771,8 @@ exports[`regression tests > key 5 selects arrow tool > [end of test] undo stack
0,
],
[
- 10,
- 10,
+ 30,
+ 30,
],
],
"roughness": 1,
@@ -8786,7 +8786,7 @@ exports[`regression tests > key 5 selects arrow tool > [end of test] undo stack
"strokeWidth": 2,
"type": "arrow",
"version": 4,
- "width": 10,
+ "width": 30,
"x": 10,
"y": 10,
},
@@ -8982,7 +8982,7 @@ exports[`regression tests > key 6 selects line tool > [end of test] undo stack 1
"fillStyle": "solid",
"frameId": null,
"groupIds": [],
- "height": 10,
+ "height": 30,
"index": "a0",
"isDeleted": false,
"lastCommittedPoint": null,
@@ -8995,8 +8995,8 @@ exports[`regression tests > key 6 selects line tool > [end of test] undo stack 1
0,
],
[
- 10,
- 10,
+ 30,
+ 30,
],
],
"polygon": false,
@@ -9009,7 +9009,7 @@ exports[`regression tests > key 6 selects line tool > [end of test] undo stack 1
"strokeWidth": 2,
"type": "line",
"version": 4,
- "width": 10,
+ "width": 30,
"x": 10,
"y": 10,
},
@@ -9167,12 +9167,12 @@ exports[`regression tests > key 7 selects freedraw tool > [end of test] undo sta
"fillStyle": "solid",
"frameId": null,
"groupIds": [],
- "height": 10,
+ "height": 30,
"index": "a0",
"isDeleted": false,
"lastCommittedPoint": [
- 10,
- 10,
+ 30,
+ 30,
],
"link": null,
"locked": false,
@@ -9183,12 +9183,12 @@ exports[`regression tests > key 7 selects freedraw tool > [end of test] undo sta
0,
],
[
- 10,
- 10,
+ 30,
+ 30,
],
[
- 10,
- 10,
+ 30,
+ 30,
],
],
"pressures": [
@@ -9204,7 +9204,7 @@ exports[`regression tests > key 7 selects freedraw tool > [end of test] undo sta
"strokeWidth": 2,
"type": "freedraw",
"version": 4,
- "width": 10,
+ "width": 30,
"x": 10,
"y": 10,
},
@@ -9401,7 +9401,7 @@ exports[`regression tests > key a selects arrow tool > [end of test] undo stack
"fillStyle": "solid",
"frameId": null,
"groupIds": [],
- "height": 10,
+ "height": 30,
"index": "a0",
"isDeleted": false,
"lastCommittedPoint": null,
@@ -9414,8 +9414,8 @@ exports[`regression tests > key a selects arrow tool > [end of test] undo stack
0,
],
[
- 10,
- 10,
+ 30,
+ 30,
],
],
"roughness": 1,
@@ -9429,7 +9429,7 @@ exports[`regression tests > key a selects arrow tool > [end of test] undo stack
"strokeWidth": 2,
"type": "arrow",
"version": 4,
- "width": 10,
+ "width": 30,
"x": 10,
"y": 10,
},
@@ -9595,7 +9595,7 @@ exports[`regression tests > key d selects diamond tool > [end of test] undo stac
"fillStyle": "solid",
"frameId": null,
"groupIds": [],
- "height": 10,
+ "height": 30,
"index": "a0",
"isDeleted": false,
"link": null,
@@ -9608,7 +9608,7 @@ exports[`regression tests > key d selects diamond tool > [end of test] undo stac
"strokeWidth": 2,
"type": "diamond",
"version": 3,
- "width": 10,
+ "width": 30,
"x": 10,
"y": 10,
},
@@ -9804,7 +9804,7 @@ exports[`regression tests > key l selects line tool > [end of test] undo stack 1
"fillStyle": "solid",
"frameId": null,
"groupIds": [],
- "height": 10,
+ "height": 30,
"index": "a0",
"isDeleted": false,
"lastCommittedPoint": null,
@@ -9817,8 +9817,8 @@ exports[`regression tests > key l selects line tool > [end of test] undo stack 1
0,
],
[
- 10,
- 10,
+ 30,
+ 30,
],
],
"polygon": false,
@@ -9831,7 +9831,7 @@ exports[`regression tests > key l selects line tool > [end of test] undo stack 1
"strokeWidth": 2,
"type": "line",
"version": 4,
- "width": 10,
+ "width": 30,
"x": 10,
"y": 10,
},
@@ -9997,7 +9997,7 @@ exports[`regression tests > key o selects ellipse tool > [end of test] undo stac
"fillStyle": "solid",
"frameId": null,
"groupIds": [],
- "height": 10,
+ "height": 30,
"index": "a0",
"isDeleted": false,
"link": null,
@@ -10010,7 +10010,7 @@ exports[`regression tests > key o selects ellipse tool > [end of test] undo stac
"strokeWidth": 2,
"type": "ellipse",
"version": 3,
- "width": 10,
+ "width": 30,
"x": 10,
"y": 10,
},
@@ -10168,12 +10168,12 @@ exports[`regression tests > key p selects freedraw tool > [end of test] undo sta
"fillStyle": "solid",
"frameId": null,
"groupIds": [],
- "height": 10,
+ "height": 30,
"index": "a0",
"isDeleted": false,
"lastCommittedPoint": [
- 10,
- 10,
+ 30,
+ 30,
],
"link": null,
"locked": false,
@@ -10184,12 +10184,12 @@ exports[`regression tests > key p selects freedraw tool > [end of test] undo sta
0,
],
[
- 10,
- 10,
+ 30,
+ 30,
],
[
- 10,
- 10,
+ 30,
+ 30,
],
],
"pressures": [
@@ -10205,7 +10205,7 @@ exports[`regression tests > key p selects freedraw tool > [end of test] undo sta
"strokeWidth": 2,
"type": "freedraw",
"version": 4,
- "width": 10,
+ "width": 30,
"x": 10,
"y": 10,
},
@@ -10371,7 +10371,7 @@ exports[`regression tests > key r selects rectangle tool > [end of test] undo st
"fillStyle": "solid",
"frameId": null,
"groupIds": [],
- "height": 10,
+ "height": 30,
"index": "a0",
"isDeleted": false,
"link": null,
@@ -10384,7 +10384,7 @@ exports[`regression tests > key r selects rectangle tool > [end of test] undo st
"strokeWidth": 2,
"type": "rectangle",
"version": 3,
- "width": 10,
+ "width": 30,
"x": 10,
"y": 10,
},
diff --git a/packages/excalidraw/tests/regressionTests.test.tsx b/packages/excalidraw/tests/regressionTests.test.tsx
index 9deefb8c7b..d4b5185bac 100644
--- a/packages/excalidraw/tests/regressionTests.test.tsx
+++ b/packages/excalidraw/tests/regressionTests.test.tsx
@@ -150,7 +150,7 @@ describe("regression tests", () => {
expect(h.state.activeTool.type).toBe(shape);
mouse.down(10, 10);
- mouse.up(10, 10);
+ mouse.up(30, 30);
if (shouldSelect) {
expect(API.getSelectedElement().type).toBe(shape);