mirror of
https://github.com/zhigang1992/react-native-card-stack-swiper.git
synced 2026-01-12 17:42:18 +08:00
Export fix
This commit is contained in:
4
Card.js
4
Card.js
@@ -4,7 +4,7 @@ import {
|
||||
View,
|
||||
} from 'react-native';
|
||||
|
||||
export default const Card = ({ style, children }) => (
|
||||
const Card = ({ style, children }) => (
|
||||
<View style={style} >
|
||||
{children}
|
||||
</View>);
|
||||
@@ -26,3 +26,5 @@ Card.defaultProps = {
|
||||
onSwipedTop: () => {},
|
||||
onSwipedBottom: () => {},
|
||||
}
|
||||
|
||||
export default Card;
|
||||
|
||||
453
index.js
453
index.js
@@ -1,450 +1,5 @@
|
||||
import React, { Component } from 'react';
|
||||
import PropTypes from 'prop-types'
|
||||
import {
|
||||
StyleSheet,
|
||||
View,
|
||||
Animated,
|
||||
PanResponder,
|
||||
Dimensions,
|
||||
Text
|
||||
} from 'react-native';
|
||||
import CardStack from './CardStack';
|
||||
import Card from './Card';
|
||||
|
||||
export const Card = ({ style, children }) => (
|
||||
<View style={style} >
|
||||
{children}
|
||||
</View>);
|
||||
|
||||
Card.propTypes = {
|
||||
children: PropTypes.oneOfType([PropTypes.array, PropTypes.object]).isRequired,
|
||||
style: PropTypes.oneOfType([PropTypes.number, PropTypes.object, PropTypes.array]),
|
||||
onSwipedLeft: PropTypes.func,
|
||||
onSwipedRight:PropTypes.func,
|
||||
onSwipedTop: PropTypes.func,
|
||||
onSwipedBottom: PropTypes.func,
|
||||
onSwiped: PropTypes.func,
|
||||
}
|
||||
Card.defaultProps = {
|
||||
style:{},
|
||||
onSwiped: () => {},
|
||||
onSwipedLeft: () => {},
|
||||
onSwipedRight: () => {},
|
||||
onSwipedTop: () => {},
|
||||
onSwipedBottom: () => {},
|
||||
}
|
||||
|
||||
|
||||
const { height, width } = Dimensions.get('window');
|
||||
export default class CardStack extends Component {
|
||||
|
||||
|
||||
static distance(x, y) {
|
||||
const a = Math.abs(x);
|
||||
const b = Math.abs(y);
|
||||
const c = Math.sqrt((a * a) + (b * b));
|
||||
return c;
|
||||
}
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state ={
|
||||
drag: new Animated.ValueXY({x: 0, y: 0}),
|
||||
dragDistance: new Animated.Value(0),
|
||||
sindex: 0,
|
||||
cardA: null,
|
||||
cardB: null,
|
||||
topCard: 'cardA',
|
||||
cards: [],
|
||||
touchStart: 0,
|
||||
};
|
||||
this.distance = this.constructor.distance;
|
||||
}
|
||||
|
||||
|
||||
componentWillMount() {
|
||||
this._panResponder = PanResponder.create({
|
||||
onStartShouldSetPanResponder: (evt, gestureState) => true,
|
||||
onStartShouldSetPanResponderCapture: (evt, gestureState) => true,
|
||||
onMoveShouldSetPanResponder: (evt, gestureState) => true,
|
||||
onMoveShouldSetPanResponderCapture: (evt, gestureState) => true,
|
||||
onPanResponderGrant: (evt, gestureState) => {
|
||||
this.setState({ touchStart: new Date().getTime() });
|
||||
},
|
||||
onPanResponderMove: (evt, gestureState) => {
|
||||
const dragDistance = this.distance(gestureState.dx, gestureState.dy );
|
||||
this.state.dragDistance.setValue(dragDistance);
|
||||
this.state.drag.setValue({x: gestureState.dx, y: gestureState.dy});
|
||||
},
|
||||
onPanResponderTerminationRequest: (evt, gestureState) => true,
|
||||
onPanResponderRelease: (evt, gestureState) => {
|
||||
|
||||
const currentTime = new Date().getTime();
|
||||
const swipeDuration = currentTime-this.state.touchStart;
|
||||
const { sindex } = this.state;
|
||||
const { verticalThreshold,
|
||||
horizontalThreshold,
|
||||
disableTopSwipe,
|
||||
disableLeftSwipe,
|
||||
disableRightSwipe,
|
||||
disableBottomSwipe,
|
||||
} = this.props;
|
||||
|
||||
|
||||
if ((Math.abs(gestureState.dy) > verticalThreshold) ||
|
||||
( Math.abs(gestureState.dy) > verticalThreshold*0.8 &&
|
||||
swipeDuration < 150))
|
||||
{
|
||||
|
||||
const swipeDirection = (gestureState.dy < 0) ? height * -1 : height;
|
||||
if(swipeDirection < 0 && !disableTopSwipe)
|
||||
{
|
||||
this.props.onSwipedTop(sindex);
|
||||
this._nextCard('top', gestureState.dx, swipeDirection, 200);
|
||||
}
|
||||
else if (!disableBottomSwipe)
|
||||
{
|
||||
this.props.onSwipedBottom(sindex);
|
||||
this._nextCard('bottom', gestureState.dx, swipeDirection, 200);
|
||||
}
|
||||
else
|
||||
{
|
||||
this._resetCard();
|
||||
}
|
||||
}else if ((Math.abs(gestureState.dx) > horizontalThreshold) || (Math.abs(gestureState.dx) > horizontalThreshold*0.6 && swipeDuration < 150)) {
|
||||
|
||||
const swipeDirection = (gestureState.dx < 0) ? width * -1 : width;
|
||||
if (swipeDirection < 0 && !disableLeftSwipe)
|
||||
{
|
||||
this._nextCard('left', swipeDirection, gestureState.dy, 200);
|
||||
}
|
||||
else if(!disableRightSwipe)
|
||||
{
|
||||
this.props.onSwipedRight(sindex);
|
||||
this._nextCard('right', swipeDirection, gestureState.dy, 200);
|
||||
}
|
||||
else
|
||||
{
|
||||
this._resetCard();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
this._resetCard();
|
||||
}
|
||||
},
|
||||
onPanResponderTerminate: (evt, gestureState) => {
|
||||
// Another component has become the responder, so this gesture
|
||||
// should be cancelled
|
||||
},
|
||||
onShouldBlockNativeResponder: (evt, gestureState) => {
|
||||
// Returns whether this component should block native components from becoming the JS
|
||||
// responder. Returns true by default. Is currently only supported on android.
|
||||
return true;
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
componentDidMount(){
|
||||
this.setState({
|
||||
cards: this.props.children,
|
||||
cardA: this.props.children[0],
|
||||
cardB: this.props.children[1],
|
||||
sindex: 2,
|
||||
});
|
||||
}
|
||||
|
||||
componentWillReceiveProps(nextProps){
|
||||
this.setState({
|
||||
cards: nextProps.children,
|
||||
});
|
||||
}
|
||||
|
||||
_resetCard(){
|
||||
|
||||
Animated.timing(
|
||||
this.state.dragDistance,
|
||||
{
|
||||
toValue: 0,
|
||||
duration: 200,
|
||||
}
|
||||
).start();
|
||||
Animated.spring(
|
||||
this.state.drag,
|
||||
{
|
||||
toValue: {x: 0, y: 0},
|
||||
duration: 200,
|
||||
}
|
||||
).start();
|
||||
|
||||
}
|
||||
|
||||
|
||||
goBackFromTop(){
|
||||
this._goBack('top');
|
||||
}
|
||||
|
||||
goBackFromRight(){
|
||||
this._goBack('right');
|
||||
}
|
||||
|
||||
goBackFromLeft(){
|
||||
this._goBack('left');
|
||||
}
|
||||
|
||||
goBackFromBottom(){
|
||||
this._goBack('bottom');
|
||||
}
|
||||
|
||||
_goBack(direction){
|
||||
const {cardA, cardB, cards, sindex, topCard} = this.state;
|
||||
|
||||
if((sindex-3) < 0) return;
|
||||
|
||||
let update = {};
|
||||
if(topCard === 'cardA'){
|
||||
update = {
|
||||
...update,
|
||||
cardB: cards[sindex-3]
|
||||
|
||||
}
|
||||
}else{
|
||||
update = {
|
||||
...update,
|
||||
cardA: cards[sindex-3],
|
||||
}
|
||||
}
|
||||
|
||||
this.setState({
|
||||
...update,
|
||||
topCard: (topCard === 'cardA') ? 'cardB' : 'cardA',
|
||||
sindex: sindex-1
|
||||
}, () => {
|
||||
|
||||
switch (direction) {
|
||||
case 'top':
|
||||
this.state.drag.setValue({x: 0, y: -height});
|
||||
this.state.dragDistance.setValue(height);
|
||||
break;
|
||||
case 'left':
|
||||
this.state.drag.setValue({x: -width, y: 0});
|
||||
this.state.dragDistance.setValue(width);
|
||||
break;
|
||||
case 'right':
|
||||
this.state.drag.setValue({x: width, y: 0});
|
||||
this.state.dragDistance.setValue(width);
|
||||
break;
|
||||
case 'bottom':
|
||||
this.state.drag.setValue({x: 0, y: height});
|
||||
this.state.dragDistance.setValue(width);
|
||||
break;
|
||||
default:
|
||||
|
||||
}
|
||||
|
||||
Animated.spring(
|
||||
this.state.dragDistance,
|
||||
{
|
||||
toValue: 0,
|
||||
duration: 400,
|
||||
}
|
||||
).start();
|
||||
|
||||
Animated.spring(
|
||||
this.state.drag,
|
||||
{
|
||||
toValue: {x: 0, y: 0},
|
||||
duration: 400,
|
||||
}
|
||||
).start();
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
|
||||
swipeTop(){
|
||||
this._nextCard('top', 0, -height, duration);
|
||||
}
|
||||
|
||||
swipedBottom(){
|
||||
this._nextCard('bottom', 0, height, duration);
|
||||
}
|
||||
|
||||
swipeRight(duration){
|
||||
this._nextCard('right', width, 0, duration);
|
||||
}
|
||||
|
||||
swipeLeft(duration){
|
||||
this._nextCard('left', -width, 0, duration);
|
||||
}
|
||||
|
||||
_nextCard(direction, x, y, duration=400){
|
||||
const { sindex, cards, topCard } = this.state;
|
||||
|
||||
switch (direction) {
|
||||
case 'left':
|
||||
this.props.onSwipedLeft();
|
||||
this.state.cards[sindex-2].props.onSwipedLeft();
|
||||
break;
|
||||
case 'right':
|
||||
this.props.onSwipedRight();
|
||||
this.state.cards[sindex-2].props.onSwipedRight();
|
||||
break;
|
||||
case 'top':
|
||||
this.props.onSwipedTop();
|
||||
this.state.cards[sindex-2].props.onSwipedTop();
|
||||
break;
|
||||
case 'bottom':
|
||||
this.props.onSwipedBottom();
|
||||
this.state.cards[sindex-2].props.onSwipedBottom();
|
||||
break;
|
||||
default:
|
||||
|
||||
}
|
||||
Animated.spring(
|
||||
this.state.dragDistance,
|
||||
{
|
||||
toValue: 220,
|
||||
duration,
|
||||
}
|
||||
).start();
|
||||
Animated.timing(
|
||||
this.state.drag,
|
||||
{
|
||||
toValue: { x, y },
|
||||
duration,
|
||||
}
|
||||
).start(() => {
|
||||
|
||||
this.props.onSwiped();
|
||||
const newTopCard = (topCard === 'cardA') ? 'cardB' : 'cardA';
|
||||
|
||||
let update = {};
|
||||
if(newTopCard === 'cardA') {
|
||||
update = {
|
||||
...update,
|
||||
cardB: cards[sindex]
|
||||
};
|
||||
}
|
||||
if(newTopCard === 'cardB') {
|
||||
update = {
|
||||
...update,
|
||||
cardA: cards[sindex],
|
||||
};
|
||||
}
|
||||
this.state.drag.setValue({x: 0, y:0});
|
||||
this.state.dragDistance.setValue(0);
|
||||
this.setState({
|
||||
...update,
|
||||
topCard: newTopCard,
|
||||
sindex: (this.props.loop && (sindex+1 >= cards.length)) ? 0 : sindex+1
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
render() {
|
||||
|
||||
const { secondCardZoom } = this.props;
|
||||
const { drag, dragDistance, cardA, cardB, topCard, sindex } = this.state;
|
||||
|
||||
const SC = dragDistance.interpolate({
|
||||
inputRange: [0,10, 220],
|
||||
outputRange: [secondCardZoom,secondCardZoom,1],
|
||||
extrapolate: 'clamp',
|
||||
});
|
||||
const rotate = drag.x.interpolate({
|
||||
inputRange: [-320,0,320],
|
||||
outputRange: this.props.outputRotationRange,
|
||||
extrapolate: 'clamp',
|
||||
});
|
||||
|
||||
// console.log('cardB',React.addons.cloneWithProps(cardB, { name: 'hej'}));
|
||||
return (
|
||||
<View {...this._panResponder.panHandlers} style={[{position:'relative'},this.props.style]}>
|
||||
<Animated.View style={{
|
||||
position: 'absolute',
|
||||
zIndex: (topCard === 'cardB') ? 3 : 2,
|
||||
transform: [
|
||||
{ rotate: (topCard === 'cardB') ? rotate: '0deg' },
|
||||
{translateX: (topCard === 'cardB') ? drag.x: 0},
|
||||
{translateY: (topCard === 'cardB') ? drag.y: 0},
|
||||
{ scale: (topCard === 'cardB') ? 1 : SC},
|
||||
]
|
||||
}}>
|
||||
{cardB}
|
||||
</Animated.View>
|
||||
<Animated.View style={{
|
||||
position: 'absolute',
|
||||
zIndex: (topCard === 'cardA') ? 3 : 2,
|
||||
transform: [
|
||||
{ rotate: (topCard === 'cardA') ? rotate: '0deg' },
|
||||
{translateX: (topCard === 'cardA') ? drag.x: 0},
|
||||
{translateY: (topCard === 'cardA') ? drag.y: 0},
|
||||
{ scale: (topCard === 'cardA') ? 1 : SC},
|
||||
]
|
||||
}}>
|
||||
{cardA}
|
||||
</Animated.View>
|
||||
|
||||
{this.props.renderNoMoreCards()}
|
||||
|
||||
</View>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
CardStack.propTypes = {
|
||||
|
||||
children: PropTypes.oneOfType([PropTypes.array, PropTypes.object]).isRequired,
|
||||
|
||||
style: PropTypes.oneOfType([PropTypes.number, PropTypes.object, PropTypes.array]),
|
||||
secondCardZoom: PropTypes.number,
|
||||
loop: PropTypes.bool,
|
||||
renderNoMoreCards: PropTypes.func,
|
||||
onSwiped: PropTypes.func,
|
||||
onSwipedLeft: PropTypes.func,
|
||||
onSwipedRight:PropTypes.func,
|
||||
onSwipedTop: PropTypes.func,
|
||||
onSwipedBottom: PropTypes.func,
|
||||
onSwiped: PropTypes.func,
|
||||
onSwipedAll: PropTypes.func,
|
||||
|
||||
disableBottomSwipe: PropTypes.bool,
|
||||
disableLeftSwipe: PropTypes.bool,
|
||||
disableRightSwipe: PropTypes.bool,
|
||||
disableTopSwipe: PropTypes.bool,
|
||||
verticalSwipe: PropTypes.bool,
|
||||
verticalThreshold: PropTypes.number,
|
||||
|
||||
horizontalSwipe: PropTypes.bool,
|
||||
horizontalThreshold: PropTypes.number,
|
||||
outputRotationRange: PropTypes.array,
|
||||
|
||||
}
|
||||
|
||||
CardStack.defaultProps = {
|
||||
|
||||
style:{},
|
||||
secondCardZoom: 0.95,
|
||||
loop: false,
|
||||
renderNoMoreCards: () => { return (<Text>No More Cards</Text>)},
|
||||
onSwiped: () => {},
|
||||
onSwipedLeft: () => {},
|
||||
onSwipedRight: () => {},
|
||||
onSwipedTop: () => {},
|
||||
onSwipedBottom: () => {},
|
||||
onSwipedAll: () => {
|
||||
console.log('onSwipedAll')
|
||||
},
|
||||
|
||||
disableBottomSwipe: true,
|
||||
disableLeftSwipe: true,
|
||||
disableRightSwipe: true,
|
||||
disableTopSwipe: true,
|
||||
verticalSwipe: true,
|
||||
verticalThreshold: height/4,
|
||||
orizontalSwipe: true,
|
||||
horizontalThreshold: width/2,
|
||||
outputRotationRange: ['-15deg','0deg','15deg'],
|
||||
|
||||
|
||||
}
|
||||
export default CardStack;
|
||||
export { Card as Card };
|
||||
|
||||
Reference in New Issue
Block a user