rc-hammer
Version:
use hammerjs in React/ReactNative
253 lines (218 loc) • 6.38 kB
JavaScript
import each from "./utils/each";
import inArray from "./utils/in-array";
import invokeArrayArg from "./utils/invoke-array-arg";
import splitStr from "./utils/split-str";
import prefixed from "./utils/prefixed";
import assign from "./utils/assign";
import Guesture from "./guesture";
import createInputInstance from "./inputjs/create-input-instance";
import Recognizer from './recognizerjs/recognizer-constructor';
import {
STATE_BEGAN,
STATE_ENDED,
STATE_CHANGED,
STATE_RECOGNIZED
} from './recognizerjs/recognizer-consts';
const STOP = 1;
const FORCED_STOP = 2;
export default class Manager {
constructor(options) {
this.options = assign({}, Guesture.default, options || {});
this.handlers = {};
this.session = {};
this.recognizers = [];
this.input = createInputInstance(this);
each(
this.options.recognizers,
item => {
let recognizer = this.add(new item[0](item[1]));
item[2] && recognizer.recognizeWith(item[2]);
item[3] && recognizer.requireFailure(item[3]);
}, this);
}
recognize(inputData) {
let { session } = this;
if (session.stopped) {
return;
}
// run the touch-action polyfill
// this.touchAction.preventDefaults(inputData);
let recognizer;
let { recognizers } = this;
// this holds the recognizer that is being recognized.
// so the recognizer's state needs to be BEGAN, CHANGED, ENDED or RECOGNIZED
// if no recognizer is detecting a thing, it is set to `null`
let { curRecognizer } = session;
// reset when the last recognizer is recognized
// or when we're in a new session
if (
!curRecognizer ||
(curRecognizer && curRecognizer.state & STATE_RECOGNIZED)
) {
curRecognizer = session.curRecognizer = null;
}
let i = 0;
while (i < recognizers.length) {
recognizer = recognizers[i];
// find out if we are allowed try to recognize the input for this one.
// 1. allow if the session is NOT forced stopped (see the .stop() method)
// 2. allow if we still haven't recognized a gesture in this session, or the this recognizer is the one
// that is being recognized.
// 3. allow if the recognizer is allowed to run simultaneous with the current recognized recognizer.
// this can be setup with the `recognizeWith()` method on the recognizer.
if (
session.stopped !== FORCED_STOP && // 1
(!curRecognizer ||
recognizer === curRecognizer || // 2
recognizer.canRecognizeWith(curRecognizer))
) {
// 3
recognizer.recognize(inputData);
} else {
recognizer.reset();
}
// if the recognizer has been recognizing the input as a valid gesture, we want to store this one as the
// current active recognizer. but only if we don't already have an active recognizer
if (
!curRecognizer &&
recognizer.state & (STATE_BEGAN | STATE_CHANGED | STATE_ENDED)
) {
curRecognizer = session.curRecognizer = recognizer;
}
i++;
}
}
add(recognizer) {
if (invokeArrayArg(recognizer, "add", this)) {
return this;
}
// remove existing
let existing = this.get(recognizer.options.event);
if (existing) {
this.remove(existing);
}
this.recognizers.push(recognizer);
recognizer.manager = this;
// this.touchAction.update();
return recognizer;
}
/**
* @private
* remove a recognizer by name or instance
* @param {Recognizer|String} recognizer
* @returns {Manager}
*/
remove(recognizer) {
if (invokeArrayArg(recognizer, "remove", this)) {
return this;
}
recognizer = this.get(recognizer);
// let's make sure this recognizer exists
if (recognizer) {
let { recognizers } = this;
let index = inArray(recognizers, recognizer);
if (index !== -1) {
recognizers.splice(index, 1);
// this.touchAction.update();
}
}
return this;
}
/**
* @private
* get a recognizer by its event name.
* @param {Recognizer|String} recognizer
* @returns {Recognizer|Null}
*/
get(recognizer) {
if (recognizer instanceof Recognizer) {
return recognizer;
}
let { recognizers } = this;
for (let i = 0; i < recognizers.length; i++) {
if (recognizers[i].options.event === recognizer) {
return recognizers[i];
}
}
return null;
}
/**
* @private
* bind event
* @param {String} events
* @param {Function} handler
* @returns {EventEmitter} this
*/
on(events, handler) {
if (events === undefined) {
return;
}
if (handler === undefined) {
return;
}
let { handlers } = this;
each(splitStr(events), (event) => {
handlers[event] = handlers[event] || [];
handlers[event].push(handler);
});
return this;
}
/**
* @private unbind event, leave emit blank to remove all handlers
* @param {String} events
* @param {Function} [handler]
* @returns {EventEmitter} this
*/
off(events, handler) {
if (events === undefined) {
return;
}
let { handlers } = this;
each(splitStr(events), (event) => {
if (!handler) {
delete handlers[event];
} else {
handlers[event] && handlers[event].splice(inArray(handlers[event], handler), 1);
}
});
return this;
}
/**
* @private emit event to the listeners
* @param {String} event
* @param {Object} data
*/
emit(event, data) {
// we also want to trigger dom events
// if (this.options.domEvents) {
// triggerDomEvent(event, data);
// }
// no handlers, so skip it all
let handlers = this.handlers[event] && this.handlers[event].slice();
if (!handlers || !handlers.length) {
return;
}
data.type = event;
data.preventDefault = function() {
data.srcEvent.preventDefault();
};
let i = 0;
while (i < handlers.length) {
handlers[i](data);
i++;
}
}
/**
* @private
* destroy the manager and unbinds all events
* it doesn't unbind dom events, that is the user own responsibility
*/
destroy() {
this.handlers = {};
this.session = {};
this.input.destroy();
}
result() {
return this.input.getOutput();
}
}