mirror of
https://github.com/excalidraw/excalidraw
synced 2025-07-25 13:58:22 +08:00
alternatvie: keep lasso drag to only mobile
This commit is contained in:
@ -25,6 +25,18 @@ export const isIOS =
|
||||
export const isBrave = () =>
|
||||
(navigator as any).brave?.isBrave?.name === "isBrave";
|
||||
|
||||
// Mobile user agent detection
|
||||
export const isMobileUA =
|
||||
/android|webos|iphone|ipad|ipod|blackberry|iemobile|opera mini/i.test(
|
||||
navigator.userAgent.toLowerCase(),
|
||||
);
|
||||
|
||||
// Mobile platform detection
|
||||
export const isMobilePlatform =
|
||||
/android|ios|iphone|ipad|ipod|blackberry|windows phone/i.test(
|
||||
navigator.platform.toLowerCase(),
|
||||
);
|
||||
|
||||
export const supportsResizeObserver =
|
||||
typeof window !== "undefined" && "ResizeObserver" in window;
|
||||
|
||||
|
@ -100,6 +100,8 @@ import {
|
||||
randomInteger,
|
||||
CLASSES,
|
||||
Emitter,
|
||||
isMobileUA,
|
||||
isMobilePlatform,
|
||||
} from "@excalidraw/common";
|
||||
|
||||
import {
|
||||
@ -2386,6 +2388,19 @@ class App extends React.Component<AppProps, AppState> {
|
||||
}
|
||||
};
|
||||
|
||||
private isMobileOrTablet = (): boolean => {
|
||||
// Touch + pointer accuracy
|
||||
const hasTouch = "ontouchstart" in window || navigator.maxTouchPoints > 0;
|
||||
const hasCoarsePointer = window.matchMedia("(pointer: coarse)").matches;
|
||||
const isTouchMobile = hasTouch && hasCoarsePointer;
|
||||
|
||||
// At least two indicators should be true
|
||||
const indicators = [isMobileUA, isTouchMobile, isMobilePlatform];
|
||||
const hasMultipleIndicators = indicators.filter(Boolean).length >= 2;
|
||||
|
||||
return hasMultipleIndicators;
|
||||
};
|
||||
|
||||
private isMobileBreakpoint = (width: number, height: number) => {
|
||||
return (
|
||||
width < MQ_MAX_WIDTH_PORTRAIT ||
|
||||
@ -6013,7 +6028,7 @@ class App extends React.Component<AppProps, AppState> {
|
||||
if (
|
||||
hasDeselectedButton ||
|
||||
(this.state.activeTool.type !== "selection" &&
|
||||
this.state.activeTool.type !== "lasso" &&
|
||||
(this.state.activeTool.type !== "lasso" || !this.isMobileOrTablet()) &&
|
||||
this.state.activeTool.type !== "text" &&
|
||||
this.state.activeTool.type !== "eraser")
|
||||
) {
|
||||
@ -6187,7 +6202,7 @@ class App extends React.Component<AppProps, AppState> {
|
||||
) {
|
||||
if (
|
||||
this.state.activeTool.type !== "lasso" ||
|
||||
selectedElements.length > 0
|
||||
(selectedElements.length > 0 && this.isMobileOrTablet())
|
||||
) {
|
||||
setCursor(this.interactiveCanvas, CURSOR_TYPE.MOVE);
|
||||
}
|
||||
@ -6301,7 +6316,8 @@ class App extends React.Component<AppProps, AppState> {
|
||||
) {
|
||||
if (
|
||||
this.state.activeTool.type !== "lasso" ||
|
||||
Object.keys(this.state.selectedElementIds).length > 0
|
||||
(Object.keys(this.state.selectedElementIds).length > 0 &&
|
||||
this.isMobileOrTablet())
|
||||
) {
|
||||
setCursor(this.interactiveCanvas, CURSOR_TYPE.MOVE);
|
||||
}
|
||||
@ -6315,7 +6331,8 @@ class App extends React.Component<AppProps, AppState> {
|
||||
) {
|
||||
if (
|
||||
this.state.activeTool.type !== "lasso" ||
|
||||
Object.keys(this.state.selectedElementIds).length > 0
|
||||
(Object.keys(this.state.selectedElementIds).length > 0 &&
|
||||
this.isMobileOrTablet())
|
||||
) {
|
||||
setCursor(this.interactiveCanvas, CURSOR_TYPE.MOVE);
|
||||
}
|
||||
@ -6583,13 +6600,16 @@ class App extends React.Component<AppProps, AppState> {
|
||||
const hitSelectedElement =
|
||||
pointerDownState.hit.element &&
|
||||
this.isASelectedElement(pointerDownState.hit.element);
|
||||
const isMobileOrTablet = this.isMobileOrTablet();
|
||||
|
||||
// Start a new lasso ONLY if we're not interacting with an existing
|
||||
// On PCs, we always want to start a new lasso path even when we're hitting some elements
|
||||
// Otherwise, start a new lasso ONLY if we're not interacting with an existing
|
||||
// selection (move/resize/rotate).
|
||||
if (
|
||||
!pointerDownState.hit.hasHitCommonBoundingBoxOfSelectedElements &&
|
||||
!isMobileOrTablet ||
|
||||
(!pointerDownState.hit.hasHitCommonBoundingBoxOfSelectedElements &&
|
||||
!pointerDownState.resize.handleType &&
|
||||
!hitSelectedElement
|
||||
!hitSelectedElement)
|
||||
) {
|
||||
this.lassoTrail.startPath(
|
||||
pointerDownState.origin.x,
|
||||
@ -6598,8 +6618,13 @@ class App extends React.Component<AppProps, AppState> {
|
||||
);
|
||||
}
|
||||
|
||||
// For lasso tool, if we hit an element, select it immediately like normal selection
|
||||
if (pointerDownState.hit.element && !hitSelectedElement) {
|
||||
// When mobile & tablet, for lasso tool
|
||||
// if we hit an element, select it immediately like normal selection
|
||||
if (
|
||||
isMobileOrTablet &&
|
||||
pointerDownState.hit.element &&
|
||||
!hitSelectedElement
|
||||
) {
|
||||
this.setState((prevState) => {
|
||||
const nextSelectedElementIds: { [id: string]: true } = {
|
||||
...prevState.selectedElementIds,
|
||||
@ -7141,7 +7166,7 @@ class App extends React.Component<AppProps, AppState> {
|
||||
): boolean => {
|
||||
if (
|
||||
this.state.activeTool.type === "selection" ||
|
||||
this.state.activeTool.type === "lasso"
|
||||
(this.state.activeTool.type === "lasso" && this.isMobileOrTablet())
|
||||
) {
|
||||
const elements = this.scene.getNonDeletedElements();
|
||||
const elementsMap = this.scene.getNonDeletedElementsMap();
|
||||
@ -8387,7 +8412,8 @@ class App extends React.Component<AppProps, AppState> {
|
||||
(hasHitASelectedElement ||
|
||||
pointerDownState.hit.hasHitCommonBoundingBoxOfSelectedElements ||
|
||||
(this.state.activeTool.type === "lasso" &&
|
||||
pointerDownState.hit.element)) &&
|
||||
pointerDownState.hit.element &&
|
||||
this.isMobileOrTablet())) &&
|
||||
!isSelectingPointsInLineEditor &&
|
||||
(this.state.activeTool.type !== "lasso" ||
|
||||
pointerDownState.hit.hasHitCommonBoundingBoxOfSelectedElements ||
|
||||
@ -8437,7 +8463,9 @@ class App extends React.Component<AppProps, AppState> {
|
||||
selectedElements.length > 0 &&
|
||||
!pointerDownState.withCmdOrCtrl &&
|
||||
!this.state.editingTextElement &&
|
||||
this.state.activeEmbeddable?.state !== "active"
|
||||
this.state.activeEmbeddable?.state !== "active" &&
|
||||
// for lasso tool, only allow dragging on mobile or tablet devices
|
||||
(this.state.activeTool.type !== "lasso" || this.isMobileOrTablet())
|
||||
) {
|
||||
const dragOffset = {
|
||||
x: pointerCoords.x - pointerDownState.drag.origin.x,
|
||||
|
Reference in New Issue
Block a user