@baleada/listenable-gestures
Version:
A collection of gesture recognizers that are compatible with Baleada Logic's Listenable class.
171 lines (153 loc) • 4.97 kB
JavaScript
import { emit, toEmitted, naiveDeepClone, getGetPoint, isDefined } from '../util';
/*
* taps is defined as a single touch that:
* - starts at a given point
* - does not move beyond a maximum distance
* - does not cancel
* - ends
* - repeats 1 time (or a minimum number of your choice), with each tap ending less than or equal to 500ms (or a maximum interval of your choice) after the previous tap ended
*/
var defaultOptions = {
minTaps: 1,
maxInterval: 500,
// Via https://ux.stackexchange.com/questions/40364/what-is-the-expected-timeframe-of-a-double-click
maxDistance: 5 // TODO: research appropriate/accessible minDistance
};
export default function taps() {
var options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
var onStart = options.onStart,
onMove = options.onMove,
onCancel = options.onCancel,
onEnd = options.onEnd,
minTaps = isDefined(options.minTaps) ? options.minTaps : defaultOptions.minTaps,
maxInterval = isDefined(options.maxInterval) ? options.maxInterval : defaultOptions.maxInterval,
maxDistance = isDefined(options.maxDistance) ? options.maxDistance : defaultOptions.maxDistance;
function touchstart(handlerApi) {
var event = handlerApi.event,
setMetadata = handlerApi.setMetadata;
setMetadata({
path: 'touchTotal',
value: event.touches.length
});
setMetadata({
path: 'lastTap.times.start',
value: event.timeStamp
});
var getPoint = getGetPoint('touch');
setMetadata({
path: 'lastTap.points.start',
value: getPoint(event)
});
emit(onStart, toEmitted(handlerApi));
}
function touchmove(handlerApi) {
emit(onMove, toEmitted(handlerApi));
}
function touchcancel(handlerApi) {
var getMetadata = handlerApi.getMetadata,
denied = handlerApi.denied;
if (getMetadata().touchTotal === 1) {
denied();
setMetadata({
path: 'touchTotal',
value: getMetadata().touchTotal - 1
}); // TODO: is there a way to un-cancel a touch without triggering a touch start? If so, this touch total calc would be wrong.
}
emit(onCancel, toEmitted(handlerApi));
}
function touchend(handlerApi) {
var event = handlerApi.event,
getMetadata = handlerApi.getMetadata,
toPolarCoordinates = handlerApi.toPolarCoordinates,
setMetadata = handlerApi.setMetadata,
pushMetadata = handlerApi.pushMetadata,
denied = handlerApi.denied;
setMetadata({
path: 'touchTotal',
value: getMetadata().touchTotal - 1
});
if (getMetadata().touchTotal === 0) {
var _getMetadata$lastTap$ = getMetadata().lastTap.points.start,
xA = _getMetadata$lastTap$.x,
yA = _getMetadata$lastTap$.y,
_event$changedTouches = event.changedTouches.item(0),
xB = _event$changedTouches.clientX,
yB = _event$changedTouches.clientY,
_toPolarCoordinates = toPolarCoordinates({
xA: xA,
xB: xB,
yA: yA,
yB: yB
}),
distance = _toPolarCoordinates.distance,
endPoint = {
x: xB,
y: yB
},
endTime = event.timeStamp;
setMetadata({
path: 'lastTap.points.end',
value: endPoint
});
setMetadata({
path: 'lastTap.times.end',
value: endTime
});
setMetadata({
path: 'lastTap.distance',
value: distance
});
if (!Array.isArray(getMetadata().taps)) {
setMetadata({
path: 'taps',
value: []
});
}
var interval = getMetadata().taps.length === 0 ? 0 : endTime - getMetadata().taps[getMetadata().taps.length - 1].times.end;
setMetadata({
path: 'lastClick.interval',
value: interval
});
var newTap = naiveDeepClone(getMetadata().lastTap);
pushMetadata({
path: 'taps',
value: newTap
});
recognize(handlerApi);
} else {
denied();
}
emit(onEnd, toEmitted(handlerApi));
}
function recognize(_ref) {
var getMetadata = _ref.getMetadata,
denied = _ref.denied,
recognized = _ref.recognized;
switch (true) {
case getMetadata().lastTap.interval > maxInterval || getMetadata().lastTap.distance > maxDistance:
// Deny after multiple touches and after taps with intervals or movement distances that are too large
var lastTap = naiveDeepClone(getMetadata().lastTap);
denied();
setMetadata({
path: 'taps',
value: []
});
pushMetadata({
path: 'taps',
value: lastTap
});
break;
default:
if (getMetadata().taps.length >= minTaps) {
recognized();
}
break;
}
}
return {
touchstart: touchstart,
touchmove: touchmove,
touchcancel: touchcancel,
touchend: touchend
};
}