react-touch
Version:
React wrapper components that make touch events easy
108 lines (88 loc) • 3.5 kB
JavaScript
import raf from 'raf';
const extractPosition = callback => (evt, ...args) => {
let nativeEvent = evt;
if (!(evt instanceof window.Event)) {
nativeEvent = evt.nativeEvent;
}
let touchPosition = null;
if (nativeEvent.touches && nativeEvent.touches.length) {
const touch = nativeEvent.touches[0];
touchPosition = { x: touch.clientX, y: touch.clientY };
} else if (nativeEvent.clientX && nativeEvent.clientY) {
touchPosition = { x: nativeEvent.clientX, y: nativeEvent.clientY };
}
return callback(touchPosition, evt, ...args);
};
class TouchHandler {
constructor(onTouchStart, onTouchMove, onTouchEnd) {
// in the event both touch and click handlers can fire (e.g., chrome device
// mode), only add one set of handlers
this._listenersAdded = false;
this._currentAnimationFrame = null;
// delegated to callbacks
this._onTouchStart = onTouchStart;
this._onTouchMove = onTouchMove;
this._onTouchEnd = onTouchEnd;
this._handleTouchStart = extractPosition(this._handleTouchStart.bind(this));
this._handleMouseDown = extractPosition(this._handleMouseDown.bind(this));
this._handleTouchMove = extractPosition(this._handleTouchMove.bind(this));
this._handleTouchEnd = extractPosition(this._handleTouchEnd.bind(this));
}
listeners(child, onTouchStart, onMouseDown) {
return {
onTouchStart: evt => this._handleTouchStart(evt, child, onTouchStart),
onMouseDown: evt => this._handleMouseDown(evt, child, onMouseDown),
};
}
removeListeners() {
this._listenersAdded = false;
document.removeEventListener('touchmove', this._handleTouchMove);
document.removeEventListener('touchend', this._handleTouchEnd);
document.removeEventListener('touchcancel', this._handleTouchEnd);
document.removeEventListener('mousemove', this._handleTouchMove);
document.removeEventListener('mouseup', this._handleTouchEnd);
}
cancelAnimationFrame() {
raf.cancel(this._currentAnimationFrame);
this._currentAnimationFrame = null;
}
_addTouchListeners() {
this._listenersAdded = true;
document.addEventListener('touchmove', this._handleTouchMove);
document.addEventListener('touchend', this._handleTouchEnd);
document.addEventListener('touchcancel', this._handleTouchEnd);
}
_addMouseListeners() {
this._listenersAdded = true;
document.addEventListener('mousemove', this._handleTouchMove);
document.addEventListener('mouseup', this._handleTouchEnd);
}
_handleTouchStart(touchPosition, synthEvent, child, onTouchStart) {
if (this._listenersAdded) return;
this._addTouchListeners();
child.props.onTouchStart && child.props.onTouchStart(synthEvent);
onTouchStart && onTouchStart(synthEvent);
this._onTouchStart(touchPosition);
}
_handleMouseDown(touchPosition, synthEvent, child, onMouseDown) {
if (this._listenersAdded) return;
this._addMouseListeners();
child.props.onMouseDown && child.props.onMouseDown(synthEvent);
onMouseDown && onMouseDown(synthEvent);
this._onTouchStart(touchPosition);
}
_handleTouchMove(touchPosition) {
if (!this._currentAnimationFrame) {
this._currentAnimationFrame = raf(() => {
this._currentAnimationFrame = null;
this._onTouchMove(touchPosition);
});
}
}
_handleTouchEnd(touchPosition) {
this.cancelAnimationFrame();
this.removeListeners();
this._onTouchEnd(touchPosition);
}
}
export default TouchHandler;