fix: improve line creation ux on touch screens (#9740)

* fix: awkward point adding and removing on touch device

* feat: move finalize to next to last point

* feat: on touch screen, click would create a default line/arrow

* fix: make default adaptive to zoom

* fix: increase padding to avoid cutoffs

* refactor: simplify

* fix: only use bigger padding when needed

* center arrow horizontally on pointer

* increase min drag distance before we start 2-point-arrow-drag-creating

* do not render 0-width arrow while creating

* dead code

* fix tests

* fix: remove redundant code

* do not enter line editor on creation

---------

Co-authored-by: dwelle <5153846+dwelle@users.noreply.github.com>
This commit is contained in:
Ryan Di
2025-07-23 18:49:56 +10:00
committed by GitHub
parent 8492b144b0
commit e5e07260c6
9 changed files with 113 additions and 102 deletions

View File

@@ -100,6 +100,7 @@ import {
randomInteger,
CLASSES,
Emitter,
MINIMUM_ARROW_SIZE,
} from "@excalidraw/common";
import {
@@ -8162,7 +8163,9 @@ class App extends React.Component<AppProps, AppState> {
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<AppProps, AppState> {
this.state,
);
if (!pointerDownState.drag.hasOccurred && newElement && !multiElement) {
this.scene.mutateElement(
newElement,
{
points: [
...newElement.points,
pointFrom<LocalPoint>(
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<LocalPoint>(0, 0),
pointFrom<LocalPoint>(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<LocalPoint>(dx, dy)],
},
{ informMutation: false, isDragging: false },
);
this.setState({
multiElement: newElement,
newElement,
});
}
} else if (pointerDownState.drag.hasOccurred && !multiElement) {
if (
isBindingEnabled(this.state) &&