jimu-mobile
Version:
积木组件库助力移动端开发
128 lines (107 loc) • 2.99 kB
JavaScript
import React from 'react';
import PropTypes from 'prop-types';
class Gestures extends React.Component {
static propTypes = {
onSwipeUp: PropTypes.func,
onSwipeDown: PropTypes.func,
onSwipeLeft: PropTypes.func,
onSwipeRight: PropTypes.func,
flickThreshold: PropTypes.number,
swipeThreshold: PropTypes.number,
};
static defaultProps = {
flickThreshold: 0.6,
swipeThreshold: 10,
};
constructor(props) {
super(props);
this.state = {
x: null,
y: null,
swiping: false,
start: 0,
};
}
_resetState() {
this.setState({
x: null, y: null, swiping: false, start: 0,
});
}
_emitEvent(name, e) {
if (this.props[name]) {
this.props[name](e);
}
}
_getGestureDetails(e) {
const { clientX, clientY } = e.changedTouches[0];
const deltaX = this.state.x - clientX;
const deltaY = this.state.y - clientY;
const absX = Math.abs(deltaX);
const absY = Math.abs(deltaY);
const duration = Date.now() - this.state.start;
const velocity = Math.sqrt(absX * absX + absY * absY) / duration;
const done = e.type === 'touchend';
e.gesture = {
deltaX, deltaY, absX, absY, velocity, duration, done,
};
return e;
}
_handleTouchStart = (e) => {
this._emitEvent('onTouchStart', e);
this.setState({
start: Date.now(),
x: e.touches[0].clientX,
y: e.touches[0].clientY,
swiping: false,
});
}
_handleTouchMove = (e) => {
const ge = this._getGestureDetails(e);
this._emitEvent('onTouchMove', ge);
if (ge.gesture.absX > this.props.swipeThreshold && ge.gesture.absY > this.props.swipeThreshold) {
this._handleSwipeGesture(ge);
}
}
_handleTouchCancel = (e) => {
this._emitEvent('onTouchCancel', e);
this._resetState();
}
_handleTouchEnd = (e) => {
const ge = this._getGestureDetails(e);
this._emitEvent('onTouchEnd', ge);
if (this.state.swiping) {
this._handleSwipeGesture(ge);
return this._resetState();
}
if (ge.gesture.duration > 0) {
this._handleTapGesture(ge);
}
this._resetState();
}
_handleTapGesture(ge) {
ge.type = 'tap';
this._emitEvent('onTap', ge);
}
_handleSwipeGesture(ge) {
const {
deltaX, absX, deltaY, absY,
} = ge.gesture;
const direction = (absX > absY)
? deltaX < 0 ? 'Right' : 'Left'
: deltaY < 0 ? 'Up' : 'Down';
this.setState({ swiping: true });
ge.gesture.isFlick = ge.gesture.velocity > this.props.flickThreshold;
ge.type = `swipe${direction.toLowerCase()}`;
this._emitEvent(`onSwipe${direction}`, ge);
ge.preventDefault();
}
render() {
return React.cloneElement(React.Children.only(this.props.children), {
onTouchStart: this._handleTouchStart,
onTouchMove: this._handleTouchMove,
onTouchCancel: this._handleTouchCancel,
onTouchEnd: this._handleTouchEnd,
});
}
}
export default Gestures;