Updated controller logic to have dynamic drag/draw mode
This commit is contained in:
parent
d51221f089
commit
1feea9778c
|
@ -31,3 +31,9 @@ ascii.Vector.prototype.add = function(other) {
|
|||
ascii.Vector.prototype.length = function() {
|
||||
return Math.sqrt(this.x * this.x + this.y * this.y);
|
||||
};
|
||||
|
||||
/** @return {ascii.Vector} */
|
||||
ascii.Vector.prototype.scale = function(scale) {
|
||||
return new ascii.Vector(this.x * scale, this.y * scale);
|
||||
};
|
||||
|
||||
|
|
|
@ -7,6 +7,9 @@ goog.provide('ascii.Controller');
|
|||
goog.require('ascii.Vector');
|
||||
goog.require('ascii.View');
|
||||
|
||||
/** @const */ var DRAG_LATENCY = 200; // Milliseconds.
|
||||
/** @const */ var DRAG_ACCURACY = 5; // Pixels.
|
||||
|
||||
/**
|
||||
* @constructor
|
||||
*/
|
||||
|
@ -14,32 +17,56 @@ ascii.Controller = function(view, state) {
|
|||
/** @type {ascii.View} */ this.view = view;
|
||||
/** @type {ascii.State} */ this.state = state;
|
||||
|
||||
/** @type {ascii.Vector} */ this.dragOrigin;
|
||||
/** @type {ascii.Vector} */ this.pressVector;
|
||||
/** @type {number} */ this.pressTimestamp;
|
||||
|
||||
this.installDesktopBindings();
|
||||
};
|
||||
|
||||
ascii.Controller.prototype.handlePress = function(x, y) {
|
||||
this.pressVector = new ascii.Vector(x, y);
|
||||
this.pressTimestamp = $.now();
|
||||
};
|
||||
|
||||
ascii.Controller.prototype.handleMove = function(x, y) {
|
||||
if (this.pressVector != null) {
|
||||
// Drag has started.
|
||||
this.view.offset.x -= (x - this.pressVector.x)/this.view.zoom;
|
||||
this.view.offset.y -= (y - this.pressVector.y)/this.view.zoom;
|
||||
this.pressVector = new ascii.Vector(x, y);
|
||||
var position = new ascii.Vector(x, y);
|
||||
|
||||
if (this.pressVector == null) { return; } // No clicks, so just ignore.
|
||||
|
||||
// Initiate a drag if we have moved enough, quickly enough.
|
||||
if (this.dragOrigin == null &&
|
||||
($.now() - this.pressTimestamp) < DRAG_LATENCY &&
|
||||
position.subtract(this.pressVector).length() > DRAG_ACCURACY) {
|
||||
this.dragOrigin = this.view.offset;
|
||||
}
|
||||
|
||||
// Drag in progress, update the view origin.
|
||||
if (this.dragOrigin != null) {
|
||||
this.view.offset = this.dragOrigin.add(
|
||||
this.pressVector
|
||||
.subtract(new ascii.Vector(x, y))
|
||||
.scale(1/this.view.zoom));
|
||||
}
|
||||
|
||||
// Drag wasn't initiated in time, treat this as a drawing event.
|
||||
if (this.dragOrigin == null && ($.now() - this.pressTimestamp) >= DRAG_LATENCY) {
|
||||
// TODO: Draw stuff.
|
||||
this.state.getCell(this.view.screenToCell(position)).value = 'O';
|
||||
}
|
||||
};
|
||||
|
||||
ascii.Controller.prototype.handleRelease = function(x, y) {
|
||||
var position = new ascii.Vector(x, y);
|
||||
if (this.pressVector.equals(position)) {
|
||||
// We should handle this as a 'click' as there was no dragging involved.
|
||||
// Hand off to the state controller, as this will initiate a modification of the diagram itself.
|
||||
this.state.getCell(this.view.screenToFrame(position)).value = 'P';
|
||||
// Drag wasn't initiated in time, treat this as a drawing event.
|
||||
if (this.dragOrigin == null &&
|
||||
($.now() - this.pressTimestamp) >= DRAG_LATENCY &&
|
||||
position.subtract(this.pressVector).length() > DRAG_ACCURACY) {
|
||||
// TODO: Draw stuff.
|
||||
}
|
||||
this.pressVector = null;
|
||||
this.pressTimestamp = 0;
|
||||
this.dragOrigin = null;
|
||||
};
|
||||
|
||||
ascii.Controller.prototype.handleZoom = function(delta) {
|
||||
|
@ -62,4 +89,5 @@ ascii.Controller.prototype.installDesktopBindings = function() {
|
|||
$(this.view.canvas).mousemove(function(e) {
|
||||
controller.handleMove(e.clientX, e.clientY);
|
||||
});
|
||||
$(window).resize(function(e) { controller.view.resizeCanvas() });
|
||||
};
|
||||
|
|
|
@ -32,10 +32,6 @@ ascii.State = function() {
|
|||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {ascii.Vector} vector
|
||||
* @return {ascii.Cell}
|
||||
*/
|
||||
ascii.State.prototype.getCell = function(vector) {
|
||||
return this.cells[Math.round((vector.x-7.5)/15)][Math.round((vector.y+7.5)/15)];
|
||||
return this.cells[vector.x][vector.y];
|
||||
};
|
||||
|
|
|
@ -1,20 +1,19 @@
|
|||
/**
|
||||
* Functions relating to view operations and management of the screen.
|
||||
*/
|
||||
goog.provide('ascii.View');
|
||||
|
||||
goog.require('ascii.Vector');
|
||||
|
||||
/** @const */ ascii.CHARACTER_PIXELS = 15;
|
||||
|
||||
|
||||
/**
|
||||
* Object relating to view operations and management of the screen.
|
||||
* @constructor
|
||||
*/
|
||||
ascii.View = function(state) {
|
||||
/** @type {Element} */ this.canvas = document.getElementById('ascii-canvas');
|
||||
/** @type {Object} */ this.context = this.canvas.getContext('2d');
|
||||
/** @type {number} */ this.zoom = 1;
|
||||
/** @type {ascii.Vector} */ this.offset = new ascii.Vector(2000, 2000);
|
||||
/** @type {ascii.Vector} */ this.offset = new ascii.Vector(7500, 7500);
|
||||
/** @type {ascii.State} */ this.state = state;
|
||||
this.resizeCanvas();
|
||||
};
|
||||
|
@ -24,12 +23,18 @@ ascii.View.prototype.resizeCanvas = function() {
|
|||
this.canvas.height = document.documentElement.clientHeight;
|
||||
};
|
||||
|
||||
/**
|
||||
* Starts the animation loop for the canvas. Should only be called once.
|
||||
*/
|
||||
ascii.View.prototype.animate = function() {
|
||||
this.render();
|
||||
var view = this;
|
||||
window.requestAnimationFrame(function() { view.animate(); });
|
||||
};
|
||||
|
||||
/**
|
||||
* Renders the given state to the canvas.
|
||||
*/
|
||||
ascii.View.prototype.render = function() {
|
||||
this.context.setTransform(1, 0, 0, 1, 0, 0);
|
||||
// Clear the visible area.
|
||||
|
@ -38,9 +43,11 @@ ascii.View.prototype.render = function() {
|
|||
this.context.scale(this.zoom, this.zoom);
|
||||
this.context.translate(this.canvas.width/2/this.zoom, this.canvas.height/2/this.zoom);
|
||||
|
||||
// TODO: Only render grid lines and cells that are visible.
|
||||
|
||||
// Render the grid.
|
||||
this.context.lineWidth="1";
|
||||
this.context.strokeStyle="#DDDDDD";
|
||||
this.context.strokeStyle="#EEEEEE";
|
||||
this.context.beginPath();
|
||||
for (var i = 0; i < this.state.cells.length; i++) {
|
||||
this.context.moveTo(
|
||||
|
@ -66,8 +73,8 @@ ascii.View.prototype.render = function() {
|
|||
for (var j = 0; j < this.state.cells[i].length; j++) {
|
||||
if (this.state.cells[i][j].value != null) {
|
||||
this.context.fillText(this.state.cells[i][j].value,
|
||||
i*ascii.CHARACTER_PIXELS - this.offset.x,
|
||||
j*ascii.CHARACTER_PIXELS - this.offset.y);
|
||||
i*ascii.CHARACTER_PIXELS - this.offset.x + 3,
|
||||
j*ascii.CHARACTER_PIXELS - this.offset.y - 2);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -95,4 +102,23 @@ ascii.View.prototype.frameToScreen = function(vector) {
|
|||
(vector.y - this.offset.y) * this.zoom + this.canvas.height/2);
|
||||
};
|
||||
|
||||
/**
|
||||
* Given a frame coordinate, return the indices for the nearest cell.
|
||||
* @param {ascii.Vector} vector
|
||||
* @return {ascii.Vector}
|
||||
*/
|
||||
ascii.View.prototype.frameToCell = function(vector) {
|
||||
return new ascii.Vector(Math.round((vector.x-7.5)/15), Math.round((vector.y+7.5)/15));
|
||||
};
|
||||
|
||||
/**
|
||||
* Given a screen coordinate, return the indices for the nearest cell.
|
||||
* @param {ascii.Vector} vector
|
||||
* @return {ascii.Vector}
|
||||
*/
|
||||
ascii.View.prototype.screenToCell = function(vector) {
|
||||
return this.frameToCell(this.screenToFrame(vector));
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
|
Loading…
Reference in New Issue