react-swipeable
Version:
Swipe bindings for react
241 lines (205 loc) • 6.19 kB
JavaScript
const React = require('react')
function getInitialState () {
return {
x: null,
y: null,
swiping: false,
start: 0
};
}
const Swipeable = React.createClass({
propTypes: {
onSwiped: React.PropTypes.func,
onSwiping: React.PropTypes.func,
onSwipingUp: React.PropTypes.func,
onSwipingRight: React.PropTypes.func,
onSwipingDown: React.PropTypes.func,
onSwipingLeft: React.PropTypes.func,
onSwipedUp: React.PropTypes.func,
onSwipedRight: React.PropTypes.func,
onSwipedDown: React.PropTypes.func,
onSwipedLeft: React.PropTypes.func,
flickThreshold: React.PropTypes.number,
delta: React.PropTypes.number,
preventDefaultTouchmoveEvent: React.PropTypes.bool,
stopPropagation: React.PropTypes.bool,
nodeName: React.PropTypes.string,
trackMouse: React.PropTypes.bool
},
componentWillMount: function () {
this.swipeable = getInitialState();
},
getDefaultProps: function () {
return {
flickThreshold: 0.6,
delta: 10,
preventDefaultTouchmoveEvent: true,
stopPropagation: false,
nodeName: 'div'
}
},
calculatePos: function (e) {
let x, y
// If not a touch, determine point from mouse coordinates
if (e.changedTouches) {
x = e.changedTouches[0].clientX
y = e.changedTouches[0].clientY
} else {
x = e.clientX
y = e.clientY
}
const xd = this.swipeable.x - x
const yd = this.swipeable.y - y
const axd = Math.abs(xd)
const ayd = Math.abs(yd)
const time = Date.now() - this.swipeable.start
const velocity = Math.sqrt(axd * axd + ayd * ayd) / time
return {
deltaX: xd,
deltaY: yd,
absX: axd,
absY: ayd,
velocity: velocity
}
},
eventStart: function (e) {
if (typeof this.props.onMouseDown === 'function') {
this.props.onMouseDown(e);
}
if (e.type === 'mousedown' && !this.props.trackMouse) {
return
}
if (e.touches && e.touches.length > 1) {
return
}
// If not a touch, determine point from mouse coordinates
let touches = e.touches
if (!touches) {
touches = [{ clientX: e.clientX, clientY: e.clientY }]
}
if (this.props.stopPropagation) e.stopPropagation()
this.swipeable = {
start: Date.now(),
x: touches[0].clientX,
y: touches[0].clientY,
swiping: false
};
},
eventMove: function (e) {
if (typeof this.props.onMouseMove === 'function') {
this.props.onMouseMove(e);
}
if (e.type === 'mousemove' && !this.props.trackMouse) {
return
}
if (!this.swipeable.x || !this.swipeable.y || e.touches && e.touches.length > 1) {
return
}
let cancelPageSwipe = false
const pos = this.calculatePos(e)
if (pos.absX < this.props.delta && pos.absY < this.props.delta) {
return
}
if (this.props.stopPropagation) e.stopPropagation()
if (this.props.onSwiping) {
this.props.onSwiping(e, pos.deltaX, pos.deltaY, pos.absX, pos.absY, pos.velocity)
}
if (pos.absX > pos.absY) {
if (pos.deltaX > 0) {
if (this.props.onSwipingLeft || this.props.onSwipedLeft) {
this.props.onSwipingLeft && this.props.onSwipingLeft(e, pos.absX)
cancelPageSwipe = true
}
} else {
if (this.props.onSwipingRight || this.props.onSwipedRight) {
this.props.onSwipingRight && this.props.onSwipingRight(e, pos.absX)
cancelPageSwipe = true
}
}
} else {
if (pos.deltaY > 0) {
if (this.props.onSwipingUp || this.props.onSwipedUp) {
this.props.onSwipingUp && this.props.onSwipingUp(e, pos.absY)
cancelPageSwipe = true
}
} else {
if (this.props.onSwipingDown || this.props.onSwipedDown) {
this.props.onSwipingDown && this.props.onSwipingDown(e, pos.absY)
cancelPageSwipe = true
}
}
}
this.swipeable.swiping = true;
if (cancelPageSwipe && this.props.preventDefaultTouchmoveEvent) {
e.preventDefault()
}
},
eventEnd: function (e) {
if (typeof this.props.onMouseUp === 'function') {
this.props.onMouseUp(e);
}
if (e.type === 'mouseup' && !this.props.trackMouse) {
return
}
if (this.swipeable.swiping) {
const pos = this.calculatePos(e)
if (this.props.stopPropagation) e.stopPropagation()
const isFlick = pos.velocity > this.props.flickThreshold
this.props.onSwiped && this.props.onSwiped(
e,
pos.deltaX,
pos.deltaY,
isFlick,
pos.velocity
)
if (pos.absX > pos.absY) {
if (pos.deltaX > 0) {
this.props.onSwipedLeft && this.props.onSwipedLeft(e, pos.deltaX, isFlick)
} else {
this.props.onSwipedRight && this.props.onSwipedRight(e, pos.deltaX, isFlick)
}
} else {
if (pos.deltaY > 0) {
this.props.onSwipedUp && this.props.onSwipedUp(e, pos.deltaY, isFlick)
} else {
this.props.onSwipedDown && this.props.onSwipedDown(e, pos.deltaY, isFlick)
}
}
}
this.swipeable = getInitialState();
},
render: function () {
const newProps = {
...this.props,
onTouchStart: this.eventStart,
onTouchMove: this.eventMove,
onTouchEnd: this.eventEnd,
onMouseDown: this.eventStart,
onMouseMove: this.eventMove,
onMouseUp: this.eventEnd
}
delete newProps.onSwiped
delete newProps.onSwiping
delete newProps.onSwipingUp
delete newProps.onSwipingRight
delete newProps.onSwipingDown
delete newProps.onSwipingLeft
delete newProps.onSwipedUp
delete newProps.onSwipedRight
delete newProps.onSwipedDown
delete newProps.onSwipedLeft
delete newProps.flickThreshold
delete newProps.delta
delete newProps.preventDefaultTouchmoveEvent
delete newProps.stopPropagation
delete newProps.nodeName
delete newProps.children
delete newProps.trackMouse
return React.createElement(
this.props.nodeName,
newProps,
this.props.children
);
}
})
module.exports = Swipeable