adds support for touch events, enabling touch dragging the canvas

This commit is contained in:
Renato Böhler
2021-04-11 20:57:27 -03:00
parent e7a606d987
commit ade86f215c
9 changed files with 109 additions and 8 deletions

View File

@ -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<DiagramEngine> {
})
);
// touch drags the canvas
this.registerAction(
new Action({
type: InputType.TOUCH_START,
fire: (event: ActionEvent<TouchEvent>) => {
this.transitionWithEvent(new DragCanvasState(), event);
}
})
);
this.registerAction(
new Action({
type: InputType.MOUSE_UP,

View File

@ -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<Event extends SyntheticEvent = SyntheticEvent, Model extends BaseModel = BaseModel> {

View File

@ -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 [];
}

View File

@ -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<E extends CanvasEngine = CanvasEngine> extends State<E> {
@ -63,6 +63,45 @@ export abstract class AbstractDisplacementState<E extends CanvasEngine = CanvasE
}
})
);
this.registerAction(
new Action({
type: InputType.TOUCH_START,
fire: (actionEvent: ActionEvent<React.TouchEvent>) => {
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<React.TouchEvent>) => {
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<React.TouchEvent>) => {
// when the mouse if up, we eject this state
this.eject();
}
})
);
}
abstract fireMouseMoved(event: AbstractDisplacementStateEvent);

View File

@ -90,6 +90,15 @@ export class CanvasWidget extends React.Component<DiagramProps> {
}}
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 (

View File

@ -16,6 +16,9 @@ namespace S {
export class SelectionBoxWidget extends React.Component<SelectionBoxWidgetProps> {
render() {
const { rect } = this.props;
if (!rect) return;
return (
<S.Container
style={{

View File

@ -1,6 +1,6 @@
import { State } from '../core-state/State';
import { Action, ActionEvent, InputType } from '../core-actions/Action';
import { MouseEvent } from 'react';
import { MouseEvent, TouchEvent } from 'react';
import { DragCanvasState } from './DragCanvasState';
import { SelectingState } from './SelectingState';
import { MoveItemsState } from './MoveItemsState';
@ -28,5 +28,15 @@ export class DefaultState extends State {
}
})
);
// touch drags the canvas
this.registerAction(
new Action({
type: InputType.TOUCH_START,
fire: (event: ActionEvent<TouchEvent>) => {
this.transitionWithEvent(new DragCanvasState(), event);
}
})
);
}
}

View File

@ -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,

View File

@ -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<DiagramEngine> {
}
})
);
// touch drags the canvas
this.registerAction(
new Action({
type: InputType.TOUCH_START,
fire: (event: ActionEvent<TouchEvent>) => {
this.transitionWithEvent(this.dragCanvas, event);
}
})
);
}
}