From ade86f215c2a37424a337e47c06e2d64e4f7eb8d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Renato=20B=C3=B6hler?= Date: Sun, 11 Apr 2021 20:57:27 -0300 Subject: [PATCH 1/3] adds support for touch events, enabling touch dragging the canvas --- .../demo-alternative-linking/DefaultState.ts | 12 +++++- .../src/core-actions/Action.ts | 10 ++++- .../src/core-actions/ActionEventBus.ts | 7 ++++ .../core-state/AbstractDisplacementState.ts | 41 ++++++++++++++++++- .../src/entities/canvas/CanvasWidget.tsx | 9 ++++ .../entities/selection/SelectionBoxWidget.tsx | 3 ++ .../src/states/DefaultState.ts | 12 +++++- .../src/states/SelectionBoxState.ts | 11 ++++- .../src/states/DefaultDiagramState.ts | 12 +++++- 9 files changed, 109 insertions(+), 8 deletions(-) diff --git a/diagrams-demo-gallery/demos/demo-alternative-linking/DefaultState.ts b/diagrams-demo-gallery/demos/demo-alternative-linking/DefaultState.ts index fe88ef966..0d569e8dc 100644 --- a/diagrams-demo-gallery/demos/demo-alternative-linking/DefaultState.ts +++ b/diagrams-demo-gallery/demos/demo-alternative-linking/DefaultState.ts @@ -1,4 +1,4 @@ -import { MouseEvent } from 'react'; +import { MouseEvent, TouchEvent } from 'react'; import { SelectingState, State, @@ -45,6 +45,16 @@ export class DefaultState extends State { }) ); + // touch drags the canvas + this.registerAction( + new Action({ + type: InputType.TOUCH_START, + fire: (event: ActionEvent) => { + this.transitionWithEvent(new DragCanvasState(), event); + } + }) + ); + this.registerAction( new Action({ type: InputType.MOUSE_UP, diff --git a/packages/react-canvas-core/src/core-actions/Action.ts b/packages/react-canvas-core/src/core-actions/Action.ts index 931121f18..3a95d40ae 100644 --- a/packages/react-canvas-core/src/core-actions/Action.ts +++ b/packages/react-canvas-core/src/core-actions/Action.ts @@ -1,4 +1,4 @@ -import { MouseEvent, KeyboardEvent, WheelEvent, SyntheticEvent } from 'react'; +import { MouseEvent, KeyboardEvent, WheelEvent, TouchEvent, SyntheticEvent } from 'react'; import { Toolkit } from '../Toolkit'; import { CanvasEngine } from '../CanvasEngine'; import { BaseModel } from '../core-models/BaseModel'; @@ -9,7 +9,10 @@ export enum InputType { MOUSE_MOVE = 'mouse-move', MOUSE_WHEEL = 'mouse-wheel', KEY_DOWN = 'key-down', - KEY_UP = 'key-up' + KEY_UP = 'key-up', + TOUCH_START = 'touch-start', + TOUCH_END = 'touch-end', + TOUCH_MOVE = 'touch-move' } export interface Mapping { @@ -19,6 +22,9 @@ export interface Mapping { [InputType.MOUSE_WHEEL]: WheelEvent; [InputType.KEY_DOWN]: KeyboardEvent; [InputType.KEY_UP]: KeyboardEvent; + [InputType.TOUCH_START]: TouchEvent; + [InputType.TOUCH_END]: TouchEvent; + [InputType.TOUCH_MOVE]: TouchEvent; } export interface ActionEvent { diff --git a/packages/react-canvas-core/src/core-actions/ActionEventBus.ts b/packages/react-canvas-core/src/core-actions/ActionEventBus.ts index d62e9481b..769ca397c 100644 --- a/packages/react-canvas-core/src/core-actions/ActionEventBus.ts +++ b/packages/react-canvas-core/src/core-actions/ActionEventBus.ts @@ -63,7 +63,14 @@ export class ActionEventBus { return this.getActionsForType(InputType.MOUSE_MOVE); } else if (event.type === 'wheel') { return this.getActionsForType(InputType.MOUSE_WHEEL); + } else if (event.type === 'touchstart') { + return this.getActionsForType(InputType.TOUCH_START); + } else if (event.type === 'touchend') { + return this.getActionsForType(InputType.TOUCH_END); + } else if (event.type === 'touchmove') { + return this.getActionsForType(InputType.TOUCH_MOVE); } + return []; } diff --git a/packages/react-canvas-core/src/core-state/AbstractDisplacementState.ts b/packages/react-canvas-core/src/core-state/AbstractDisplacementState.ts index d8548af0b..5a4b33257 100644 --- a/packages/react-canvas-core/src/core-state/AbstractDisplacementState.ts +++ b/packages/react-canvas-core/src/core-state/AbstractDisplacementState.ts @@ -7,7 +7,7 @@ export interface AbstractDisplacementStateEvent { displacementY: number; virtualDisplacementX: number; virtualDisplacementY: number; - event: React.MouseEvent; + event: React.MouseEvent | React.TouchEvent; } export abstract class AbstractDisplacementState extends State { @@ -63,6 +63,45 @@ export abstract class AbstractDisplacementState) => { + const touch = actionEvent.event.touches[0]; + this.initialX = touch.clientX; + this.initialY = touch.clientY; + const rel = this.engine.getRelativePoint(touch.clientX, touch.clientY); + this.initialXRelative = rel.x; + this.initialYRelative = rel.y; + } + }) + ); + this.registerAction( + new Action({ + type: InputType.TOUCH_MOVE, + fire: (actionEvent: ActionEvent) => { + const { event } = actionEvent; + const touch = event.touches[0]; + this.fireMouseMoved({ + displacementX: touch.clientX - this.initialX, + displacementY: touch.clientY - this.initialY, + virtualDisplacementX: (touch.clientX - this.initialX) / (this.engine.getModel().getZoomLevel() / 100.0), + virtualDisplacementY: (touch.clientY - this.initialY) / (this.engine.getModel().getZoomLevel() / 100.0), + event: event + }); + } + }) + ); + this.registerAction( + new Action({ + type: InputType.TOUCH_END, + fire: (event: ActionEvent) => { + // when the mouse if up, we eject this state + this.eject(); + } + }) + ); } abstract fireMouseMoved(event: AbstractDisplacementStateEvent); diff --git a/packages/react-canvas-core/src/entities/canvas/CanvasWidget.tsx b/packages/react-canvas-core/src/entities/canvas/CanvasWidget.tsx index 38e1546c0..77a06cf82 100644 --- a/packages/react-canvas-core/src/entities/canvas/CanvasWidget.tsx +++ b/packages/react-canvas-core/src/entities/canvas/CanvasWidget.tsx @@ -90,6 +90,15 @@ export class CanvasWidget extends React.Component { }} onMouseMove={(event) => { this.props.engine.getActionEventBus().fireAction({ event }); + }} + onTouchStart={(event) => { + this.props.engine.getActionEventBus().fireAction({ event }); + }} + onTouchEnd={(event) => { + this.props.engine.getActionEventBus().fireAction({ event }); + }} + onTouchMove={(event) => { + this.props.engine.getActionEventBus().fireAction({ event }); }}> {model.getLayers().map((layer) => { return ( diff --git a/packages/react-canvas-core/src/entities/selection/SelectionBoxWidget.tsx b/packages/react-canvas-core/src/entities/selection/SelectionBoxWidget.tsx index d7642e580..314dde44a 100644 --- a/packages/react-canvas-core/src/entities/selection/SelectionBoxWidget.tsx +++ b/packages/react-canvas-core/src/entities/selection/SelectionBoxWidget.tsx @@ -16,6 +16,9 @@ namespace S { export class SelectionBoxWidget extends React.Component { render() { const { rect } = this.props; + + if (!rect) return; + return ( ) => { + this.transitionWithEvent(new DragCanvasState(), event); + } + }) + ); } } diff --git a/packages/react-canvas-core/src/states/SelectionBoxState.ts b/packages/react-canvas-core/src/states/SelectionBoxState.ts index 648433418..5fa86cb22 100644 --- a/packages/react-canvas-core/src/states/SelectionBoxState.ts +++ b/packages/react-canvas-core/src/states/SelectionBoxState.ts @@ -1,7 +1,8 @@ +import { MouseEvent, TouchEvent } from 'react'; import { AbstractDisplacementState, AbstractDisplacementStateEvent } from '../core-state/AbstractDisplacementState'; import { State } from '../core-state/State'; import { SelectionLayerModel } from '../entities/selection/SelectionLayerModel'; -import { Rectangle } from '@projectstorm/geometry'; +import { Point, Rectangle } from '@projectstorm/geometry'; import { ModelGeometryInterface } from '../core/ModelGeometryInterface'; export class SelectionBoxState extends AbstractDisplacementState { @@ -26,7 +27,13 @@ export class SelectionBoxState extends AbstractDisplacementState { } getBoxDimensions(event: AbstractDisplacementStateEvent): ClientRect { - const rel = this.engine.getRelativePoint(event.event.clientX, event.event.clientY); + let rel: Point; + if (event.event instanceof MouseEvent) { + rel = this.engine.getRelativePoint(event.event.clientX, event.event.clientY); + } else if (event.event instanceof TouchEvent) { + const touch = event.event.touches[0]; + rel = this.engine.getRelativePoint(touch.clientX, touch.clientY); + } return { left: rel.x > this.initialXRelative ? this.initialXRelative : rel.x, diff --git a/packages/react-diagrams-core/src/states/DefaultDiagramState.ts b/packages/react-diagrams-core/src/states/DefaultDiagramState.ts index adadd2f37..d052e8df9 100644 --- a/packages/react-diagrams-core/src/states/DefaultDiagramState.ts +++ b/packages/react-diagrams-core/src/states/DefaultDiagramState.ts @@ -1,4 +1,4 @@ -import { MouseEvent } from 'react'; +import { MouseEvent, TouchEvent } from 'react'; import { SelectingState, State, @@ -48,5 +48,15 @@ export class DefaultDiagramState extends State { } }) ); + + // touch drags the canvas + this.registerAction( + new Action({ + type: InputType.TOUCH_START, + fire: (event: ActionEvent) => { + this.transitionWithEvent(this.dragCanvas, event); + } + }) + ); } } From 44fae73a520c52b165dcd32928507481a9bf36b9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Renato=20B=C3=B6hler?= Date: Thu, 15 Apr 2021 19:16:31 -0300 Subject: [PATCH 2/3] removes code duplication on AbstractDisplacementState --- .../core-state/AbstractDisplacementState.ts | 66 +++++++++---------- 1 file changed, 32 insertions(+), 34 deletions(-) diff --git a/packages/react-canvas-core/src/core-state/AbstractDisplacementState.ts b/packages/react-canvas-core/src/core-state/AbstractDisplacementState.ts index 5a4b33257..b320a1fb5 100644 --- a/packages/react-canvas-core/src/core-state/AbstractDisplacementState.ts +++ b/packages/react-canvas-core/src/core-state/AbstractDisplacementState.ts @@ -22,11 +22,8 @@ export abstract class AbstractDisplacementState) => { - this.initialX = actionEvent.event.clientX; - this.initialY = actionEvent.event.clientY; - const rel = this.engine.getRelativePoint(actionEvent.event.clientX, actionEvent.event.clientY); - this.initialXRelative = rel.x; - this.initialYRelative = rel.y; + const { clientX, clientY } = actionEvent.event; + this.handleMoveStart(clientX, clientY); } }) ); @@ -44,23 +41,15 @@ export abstract class AbstractDisplacementState) => { - // when the mouse if up, we eject this state - this.eject(); - } + fire: () => this.handleMoveEnd(), }) ); @@ -68,12 +57,8 @@ export abstract class AbstractDisplacementState) => { - const touch = actionEvent.event.touches[0]; - this.initialX = touch.clientX; - this.initialY = touch.clientY; - const rel = this.engine.getRelativePoint(touch.clientX, touch.clientY); - this.initialXRelative = rel.x; - this.initialYRelative = rel.y; + const { clientX, clientY } = actionEvent.event.touches[0]; + this.handleMoveStart(clientX, clientY); } }) ); @@ -82,27 +67,40 @@ export abstract class AbstractDisplacementState) => { const { event } = actionEvent; - const touch = event.touches[0]; - this.fireMouseMoved({ - displacementX: touch.clientX - this.initialX, - displacementY: touch.clientY - this.initialY, - virtualDisplacementX: (touch.clientX - this.initialX) / (this.engine.getModel().getZoomLevel() / 100.0), - virtualDisplacementY: (touch.clientY - this.initialY) / (this.engine.getModel().getZoomLevel() / 100.0), - event: event - }); + const { clientX, clientY } = event.touches[0]; + this.handleMove(clientX, clientY, event); } }) ); this.registerAction( new Action({ type: InputType.TOUCH_END, - fire: (event: ActionEvent) => { - // when the mouse if up, we eject this state - this.eject(); - } + fire: () => this.handleMoveEnd(), }) ); } + protected handleMoveStart(x: number, y: number): void { + this.initialX = x; + this.initialY = y; + const rel = this.engine.getRelativePoint(x, y); + this.initialXRelative = rel.x; + this.initialYRelative = rel.y; + } + + protected handleMove(x: number, y: number, event: React.MouseEvent | React.TouchEvent): void { + this.fireMouseMoved({ + displacementX: x - this.initialX, + displacementY: y - this.initialY, + virtualDisplacementX: (x - this.initialX) / (this.engine.getModel().getZoomLevel() / 100.0), + virtualDisplacementY: (y - this.initialY) / (this.engine.getModel().getZoomLevel() / 100.0), + event, + }); + } + + protected handleMoveEnd(): void { + this.eject(); + } + abstract fireMouseMoved(event: AbstractDisplacementStateEvent); } From 1bdc6c762446a6387c89ab5f8e8f71d2dc36077d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Renato=20B=C3=B6hler?= Date: Wed, 23 Jun 2021 11:32:40 -0300 Subject: [PATCH 3/3] fixes SelectionBoxWidget returning undefined --- .../src/core-state/AbstractDisplacementState.ts | 6 +++--- .../src/entities/selection/SelectionBoxWidget.tsx | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/packages/react-canvas-core/src/core-state/AbstractDisplacementState.ts b/packages/react-canvas-core/src/core-state/AbstractDisplacementState.ts index b320a1fb5..ff2e86191 100644 --- a/packages/react-canvas-core/src/core-state/AbstractDisplacementState.ts +++ b/packages/react-canvas-core/src/core-state/AbstractDisplacementState.ts @@ -49,7 +49,7 @@ export abstract class AbstractDisplacementState this.handleMoveEnd(), + fire: () => this.handleMoveEnd() }) ); @@ -75,7 +75,7 @@ export abstract class AbstractDisplacementState this.handleMoveEnd(), + fire: () => this.handleMoveEnd() }) ); } @@ -94,7 +94,7 @@ export abstract class AbstractDisplacementState render() { const { rect } = this.props; - if (!rect) return; + if (!rect) return null; return (