react-native-komect-uikit
Version:
React Native UI Toolkit
147 lines (125 loc) • 3.56 kB
JavaScript
import React, { PropTypes, Component } from 'react';
import {
View,
Animated,
PanResponder,
Dimensions,
StyleSheet,
LayoutAnimation,
UIManager,
} from 'react-native';
const SCREEN_WIDTH = Dimensions.get('window').width;
const SWIPE_THRESHOLD = 0.4 * SCREEN_WIDTH;
export default class SwipeDeck extends Component {
static defaultProps = {
onSwipeRight: () => {},
onSwipeLeft: () => {},
};
constructor(props) {
super(props);
const position = new Animated.ValueXY();
const panResponder = PanResponder.create({
onStartShouldSetPanResponder: () => true,
onPanResponderMove: (event, gesture) => {
position.setValue({ x: gesture.dx, y: gesture.dy });
},
onPanResponderRelease: (event, gesture) => {
if (gesture.dx > SWIPE_THRESHOLD) {
this.forceSwipe('right');
} else if (gesture.dx < -SWIPE_THRESHOLD) {
this.forceSwipe('left');
} else {
this.resetPosition();
}
},
});
this.state = { panResponder, position, index: 0 };
}
componentWillReceiveProps(nextProps) {
if (nextProps.data !== this.props.data) {
this.setState({ index: 0 });
}
}
componentWillUpdate() {
UIManager.setLayoutAnimationEnabledExperimental &&
UIManager.setLayoutAnimationEnabledExperimental(true);
LayoutAnimation.spring();
}
forceSwipe(direction) {
const x = direction === 'right' ? SCREEN_WIDTH : -SCREEN_WIDTH;
Animated.timing(this.state.position, {
toValue: { x: x * 2, y: direction === 'right' ? -x : x },
duration: 750,
}).start(() => this.onSwipeComplete(direction));
}
onSwipeComplete(direction) {
const { onSwipeRight, onSwipeLeft, data } = this.props;
const item = data[this.state.index];
direction === 'right' ? onSwipeRight(item) : onSwipeLeft(item);
this.state.position.setValue({ x: 0, y: 0 });
this.setState({ index: this.state.index + 1 });
}
resetPosition() {
Animated.spring(this.state.position, {
toValue: { x: 0, y: 0 },
}).start();
}
getCardStyle() {
const { position } = this.state;
const rotate = position.x.interpolate({
inputRange: [-SCREEN_WIDTH * 2, 0, SCREEN_WIDTH * 2],
outputRange: ['-60deg', '0deg', '60deg'],
});
return {
...this.state.position.getLayout(),
transform: [{ rotate }],
};
}
renderCards() {
if (this.state.index >= this.props.data.length) {
return this.props.renderNoMoreCards();
}
return this.props.data
.map((item, i) => {
if (i < this.state.index) {
return null;
} else if (i === this.state.index) {
return (
<Animated.View
key={item.id}
style={[this.getCardStyle(), styles.cardStyle]}
{...this.state.panResponder.panHandlers}
>
{this.props.renderCard(item)}
</Animated.View>
);
}
return (
<Animated.View key={item.id} style={styles.cardStyle}>
{this.props.renderCard(item)}
</Animated.View>
);
})
.reverse();
}
render() {
return (
<View>
{this.renderCards()}
</View>
);
}
}
const styles = StyleSheet.create({
cardStyle: {
position: 'absolute',
width: SCREEN_WIDTH,
},
});
SwipeDeck.propTypes = {
data: PropTypes.any,
renderCard: PropTypes.any,
renderNoMoreCards: PropTypes.any,
onSwipeRight: PropTypes.any,
onSwipeLeft: PropTypes.any,
};