asciiflow2/js-lib/input-controller.js

187 lines
5.0 KiB
JavaScript

import * as c from './constants';
import Controller from './controller';
import Vector from './vector';
/**
* Handles desktop inputs, and passes them onto the main controller.
*/
export class DesktopController {
/**
* @param {Controller} controller
*/
constructor(controller) {
/** @type {Controller} */ this.controller = controller;
/** @type {boolean} */ this.isDragging = false;
this.installBindings();
};
/**
* @param {number} delta
*/
handleZoom(delta) {
var newzoom = this.controller.view.zoom * (delta > 0 ? 1.1 : 0.9);
newzoom = Math.max(Math.min(newzoom, 5), 0.2);
this.controller.view.setZoom(newzoom);
}
/**
* Installs input bindings associated with keyboard controls.
*/
installBindings() {
var canvas = $(this.controller.view.canvas);
canvas.on('mousewheel', e => {
this.handleZoom(e.originalEvent.wheelDelta);
});
canvas.mousedown(e => {
// Can drag by holding either the control or meta (Apple) key.
if (e.ctrlKey || e.metaKey) {
this.controller.startDrag(Vector.fromMouseEvent(e));
} else {
this.controller.startDraw(Vector.fromMouseEvent(e));
}
});
// Pass these events through to the main controller.
canvas.mouseup(e => {
this.controller.endAll();
});
canvas.mouseleave(e => {
this.controller.endAll();
});
canvas.mousemove(e => {
this.controller.handleMove(Vector.fromMouseEvent(e));
});
}
}
/**
* Handles touch inputs, and passes them onto the main controller.
*/
export class TouchController {
/**
* @param {Controller} controller
*/
constructor(controller) {
/** @type {Controller} */ this.controller = controller;
/** @type {Vector} */ this.pressVector;
/** @type {number} */ this.originalZoom;
/** @type {number} */ this.zoomLength;
/** @type {number} */ this.pressTimestamp;
/** @type {boolean} */ this.dragStarted = false;
/** @type {boolean} */ this.zoomStarted = false;
this.installBindings();
}
/**
* @param {Vector} position
*/
handlePress(position) {
this.pressVector = position;
this.pressTimestamp = $.now();
this.dragStarted = false;
// If a drag or zoom didn't start and if we didn't release already, then handle it as a draw.
window.setTimeout(() => {
if (!this.dragStarted && !this.zoomStarted && this.pressVector != null) {
this.controller.startDraw(position);
}
}, c.DRAG_LATENCY);
}
/**
* The multi-touch version of handlePress.
* @param {Vector} positionOne
* @param {Vector} positionTwo
*/
handlePressMulti(positionOne, positionTwo) {
// A second finger as been placed, cancel whatever we were doing.
this.controller.endAll();
this.zoomStarted = true;
this.dragStarted = false;
this.zoomLength = positionOne.subtract(positionTwo).length();
this.originalZoom = this.controller.view.zoom;
}
/**
* @param {Vector} position
*/
handleMove(position) {
// Initiate a drag if we have moved enough, quickly enough.
if (!this.dragStarted &&
($.now() - this.pressTimestamp) < c.DRAG_LATENCY &&
position.subtract(this.pressVector).length() > c.DRAG_ACCURACY) {
this.dragStarted = true;
this.controller.startDrag(position);
}
// Pass on the event.
this.controller.handleMove(position);
}
/**
* The multi-touch version of handleMove, effectively only deals with zooming.
* @param {Vector} positionOne
* @param {Vector} positionTwo
*/
handleMoveMulti(positionOne, positionTwo) {
if (this.zoomStarted) {
var newZoom = this.originalZoom *
positionOne.subtract(positionTwo).length() / this.zoomLength;
newZoom = Math.max(Math.min(newZoom, 5), 0.5);
this.controller.view.setZoom(newZoom);
}
}
/**
* Ends all current actions, cleans up any state.
*/
reset() {
this.dragStarted = false;
this.zoomStarted = false;
this.pressVector = null;
}
/**
* Installs input bindings associated with touch controls.
*/
installBindings() {
var canvas = $(this.controller.view.canvas);
canvas.on('touchstart', e => {
e.preventDefault();
if (e.originalEvent.touches.length == 1) {
this.handlePress(Vector.fromTouchEvent(e));
} else if (e.originalEvent.touches.length > 1) {
this.handlePressMulti(
Vector.fromTouchEvent(e, 0),
Vector.fromTouchEvent(e, 1));
}
});
canvas.on('touchmove', e => {
e.preventDefault();
if (e.originalEvent.touches.length == 1) {
this.handleMove(Vector.fromTouchEvent(e));
} else if (e.originalEvent.touches.length > 1) {
this.handleMoveMulti(
Vector.fromTouchEvent(e, 0),
Vector.fromTouchEvent(e, 1));
}
});
// Pass through, no special handling.
canvas.on('touchend', e => {
e.preventDefault();
this.reset();
this.controller.endAll();
});
}
}