tapspace
Version:
A zoomable user interface lib for web apps
149 lines (125 loc) • 4.36 kB
JavaScript
/*
Touchable
An InputManager for touch events and mouse drag.
In comparision to Recognizer and Sensor, this module
defines the public methods, API.
Notes:
[1] As a design detail, the handling of element could be located
within Recognizer and hidden from Touchable. However, it is
convenient for Touchable API users to access the element by touch.element.
[2] Mouse events do not provide info about where the drag started
unlike touch events. Therefore we capture mouse events on
the view container, store the target of the mousedown and
emit our custom events to be handled by the Sensor.
See utils.startMouseConversion
*/
var Emitter = require('component-emitter')
var Recognizer = require('./Recognizer')
var MouseConverter = require('./MouseConverter')
var utils = require('./utils')
// See [2]
var converter = new MouseConverter()
var Touchable = function (view, sourceItem, targetItem) {
// Make it possible to transform or press a AbstractPlane by hand.
//
// Parameters:
// view
// SpaceView of the HTMLElement to listen.
// sourceItem
// an AbstractPlane, the item to make touchable in the given view.
// The item must have an element in the view.
// targetItem
// optional AbstractPlane or function, the target for detected
// transformations. Defaults to sourceItem.
// If function is given, it is called every time a targetItem
// would be transformed. Recognized ITransform is provided as 1st arg.
// Usage example: a drag handle that can move its larger parent.
//
Emitter(this)
if (view.getContainer() === null) {
throw new Error('Touchable requires a mounted view.')
}
this.view = view
// Source item. We detect transformations from the element of this item.
this.item = sourceItem
if (typeof targetItem === 'undefined') {
this.targetItem = sourceItem
} else {
this.targetItem = targetItem
}
// For pointer events (touch, mouse), we listen to the HTMLElement of
// the given item. However, if the item is
// the view itself, we must listen to the container element
// of the view. This is because pointer events eventually
// bubble to the container, instead of the view's own 0x0 div,
// that receives only the pointer events of the children of the view.
// See also [1] for note about code structure regarding elements.
if (this.view === this.item) {
this.element = this.view.getContainer()
} else {
this.element = this.view.getElementBySpaceItem(this.item)
// Ensure the element exists
if (this.element === null) {
throw new Error('No HTMLElement found. ' +
'Ensure the item is in the same space with the view.')
}
}
// Init interaction mode.
this.mode = Touchable.DEFAULT_MODE
this._rec = null // A Recognizer. Null means deactive state.
}
Touchable.DEFAULT_MODE = {
rotate: false,
scale: false,
translate: false,
tap: false,
tapMaxTravel: 20,
pivot: null,
preventDefault: true
}
Touchable.prototype.start = function (mode) {
// Set current interaction mode. It is a combination of
// a transformation mode and a press mode.
//
// Parameters:
// mode:
// pivot: a tapspace.IVector
// preventDefault: bool
// rotate: bool
// scale: bool
// translate: bool
// tap: bool
// tapMaxTravel: number
//
// Ensure mouse event conversion is running for the view. See [2].
converter.start(this.view)
// Reset interaction mode and then add the given mode options.
this.mode = utils.extend(Touchable.DEFAULT_MODE, mode)
// Create or alternatively update the manager on the fly.
if (this._rec === null) {
this._rec = new Recognizer(this)
} else {
this._rec.update(this.mode)
}
}
Touchable.prototype.restart = function (mode) {
// Alias for start() but is more readable if recognition is really
// "re"started.
return this.start(mode)
}
Touchable.prototype.resume = function () {
// Restart with the previous mode.
if (this._rec === null) {
this.start(this.mode)
}
}
Touchable.prototype.stop = function () {
// Turn touchability off
if (this._rec !== null) {
this._rec.destroy()
this._rec = null
// Ensure that mouse conversion is stopped at the last stop call.
converter.stop(this.view)
}
}
module.exports = Touchable