UNPKG

react-native-deck-swiper

Version:

Awesome tinder like card swiper for react-native. Highly Customizable!

1,037 lines (914 loc) 30.9 kB
import React, { Component } from 'react' import { PanResponder, Text, View, Dimensions, Animated, InteractionManager } from 'react-native' import PropTypes from 'prop-types' import isEqual from 'lodash/isEqual' import styles from './styles' const { height, width } = Dimensions.get('window') const LABEL_TYPES = { NONE: 'none', LEFT: 'left', RIGHT: 'right', TOP: 'top', BOTTOM: 'bottom' } const SWIPE_MULTIPLY_FACTOR = 7 const calculateCardIndexes = (firstCardIndex, cards) => { firstCardIndex = firstCardIndex || 0 const previousCardIndex = firstCardIndex === 0 ? cards.length - 1 : firstCardIndex - 1 const secondCardIndex = firstCardIndex === cards.length - 1 ? 0 : firstCardIndex + 1 return { firstCardIndex, secondCardIndex, previousCardIndex } } const rebuildStackAnimatedValues = (props) => { const stackPositionsAndScales = {} const { stackSize, stackSeparation, stackScale } = props for (let position = 0; position < stackSize; position++) { stackPositionsAndScales[`stackPosition${position}`] = new Animated.Value(stackSeparation * position) stackPositionsAndScales[`stackScale${position}`] = new Animated.Value((100 - stackScale * position) * 0.01) } return stackPositionsAndScales } class Swiper extends Component { constructor (props) { super(props) this.state = { ...calculateCardIndexes(props.cardIndex, props.cards), pan: new Animated.ValueXY(), previousCardX: new Animated.Value(props.previousCardDefaultPositionX), previousCardY: new Animated.Value(props.previousCardDefaultPositionY), swipedAllCards: false, panResponderLocked: false, labelType: LABEL_TYPES.NONE, slideGesture: false, swipeBackXYPositions: [], isSwipingBack: false, ...rebuildStackAnimatedValues(props) } this._mounted = true this._animatedValueX = 0 this._animatedValueY = 0 this.state.pan.x.addListener(value => (this._animatedValueX = value.value)) this.state.pan.y.addListener(value => (this._animatedValueY = value.value)) this.initializeCardStyle() this.initializePanResponder() } shouldComponentUpdate = (nextProps, nextState) => { const { props, state } = this const propsChanged = ( !isEqual(props.cards, nextProps.cards) || props.cardIndex !== nextProps.cardIndex ) const stateChanged = ( nextState.firstCardIndex !== state.firstCardIndex || nextState.secondCardIndex !== state.secondCardIndex || nextState.previousCardIndex !== state.previousCardIndex || nextState.labelType !== state.labelType || nextState.swipedAllCards !== state.swipedAllCards ) return propsChanged || stateChanged } componentWillUnmountAfterInteractions = () => { this.state.pan.x.removeAllListeners() this.state.pan.y.removeAllListeners() this.dimensionsChangeSubscription?.remove() } componentWillUnmount = () => { this._mounted = false; InteractionManager.runAfterInteractions(this.componentWillUnmountAfterInteractions.bind(this)); } getCardStyle = () => { const { height, width } = Dimensions.get('window') const { cardVerticalMargin, cardHorizontalMargin, marginTop, marginBottom } = this.props const cardWidth = width - cardHorizontalMargin * 2 const cardHeight = height - cardVerticalMargin * 2 - marginTop - marginBottom return { top: cardVerticalMargin, left: cardHorizontalMargin, width: cardWidth, height: cardHeight } } initializeCardStyle = () => { // this.forceUpdate() this.dimensionsChangeSubscription = Dimensions.addEventListener('change', this.onDimensionsChange) } initializePanResponder = () => { this._panResponder = PanResponder.create({ onStartShouldSetPanResponder: (event, gestureState) => true, onMoveShouldSetPanResponder: (event, gestureState) => false, onMoveShouldSetPanResponderCapture: (evt, gestureState) => { const isVerticalSwipe = Math.sqrt( Math.pow(gestureState.dx, 2) < Math.pow(gestureState.dy, 2) ) if (!this.props.verticalSwipe && isVerticalSwipe) { return false } return Math.sqrt(Math.pow(gestureState.dx, 2) + Math.pow(gestureState.dy, 2)) > 10 }, onPanResponderGrant: this.onPanResponderGrant, onPanResponderMove: this.onPanResponderMove, onPanResponderRelease: this.onPanResponderRelease, onPanResponderTerminate: this.onPanResponderRelease }) } createAnimatedEvent = () => { const { horizontalSwipe, verticalSwipe } = this.props const { x, y } = this.state.pan const dx = horizontalSwipe ? x : new Animated.Value(0) const dy = verticalSwipe ? y : new Animated.Value(0) return { dx, dy } } onDimensionsChange = () => { this.forceUpdate() } onPanResponderMove = (event, gestureState) => { this.props.onSwiping(this._animatedValueX, this._animatedValueY) let { overlayOpacityHorizontalThreshold, overlayOpacityVerticalThreshold } = this.props if (!overlayOpacityHorizontalThreshold) { overlayOpacityHorizontalThreshold = this.props.horizontalThreshold } if (!overlayOpacityVerticalThreshold) { overlayOpacityVerticalThreshold = this.props.verticalThreshold } let isSwipingLeft, isSwipingRight, isSwipingTop, isSwipingBottom if (Math.abs(this._animatedValueX) > Math.abs(this._animatedValueY) && Math.abs(this._animatedValueX) > overlayOpacityHorizontalThreshold) { if (this._animatedValueX > 0) isSwipingRight = true else isSwipingLeft = true } else if (Math.abs(this._animatedValueY) > Math.abs(this._animatedValueX) && Math.abs(this._animatedValueY) > overlayOpacityVerticalThreshold) { if (this._animatedValueY > 0) isSwipingBottom = true else isSwipingTop = true } if (isSwipingRight) { this.setState({ labelType: LABEL_TYPES.RIGHT }) } else if (isSwipingLeft) { this.setState({ labelType: LABEL_TYPES.LEFT }) } else if (isSwipingTop) { this.setState({ labelType: LABEL_TYPES.TOP }) } else if (isSwipingBottom) { this.setState({ labelType: LABEL_TYPES.BOTTOM }) } else { this.setState({ labelType: LABEL_TYPES.NONE }) } const { onTapCardDeadZone } = this.props if ( this._animatedValueX < -onTapCardDeadZone || this._animatedValueX > onTapCardDeadZone || this._animatedValueY < -onTapCardDeadZone || this._animatedValueY > onTapCardDeadZone ) { this.setState({ slideGesture: true }) } return Animated.event([null, this.createAnimatedEvent()], { useNativeDriver: false })( event, gestureState ) } onPanResponderGrant = (event, gestureState) => { this.props.dragStart && this.props.dragStart() if (!this.state.panResponderLocked) { this.state.pan.setOffset({ x: 0, y: 0 }) } this.state.pan.setValue({ x: 0, y: 0 }) } validPanResponderRelease = () => { const { disableBottomSwipe, disableLeftSwipe, disableRightSwipe, disableTopSwipe } = this.props const { isSwipingLeft, isSwipingRight, isSwipingTop, isSwipingBottom } = this.getSwipeDirection(this._animatedValueX, this._animatedValueY) return ( (isSwipingLeft && !disableLeftSwipe) || (isSwipingRight && !disableRightSwipe) || (isSwipingTop && !disableTopSwipe) || (isSwipingBottom && !disableBottomSwipe) ) } onPanResponderRelease = (e, gestureState) => { this.props.dragEnd && this.props.dragEnd() if (this.state.panResponderLocked) { this.state.pan.setValue({ x: 0, y: 0 }) this.state.pan.setOffset({ x: 0, y: 0 }) return } const { horizontalThreshold, verticalThreshold } = this.props const animatedValueX = Math.abs(this._animatedValueX) const animatedValueY = Math.abs(this._animatedValueY) const isSwiping = animatedValueX > horizontalThreshold || animatedValueY > verticalThreshold if (isSwiping && this.validPanResponderRelease()) { const onSwipeDirectionCallback = this.getOnSwipeDirectionCallback( this._animatedValueX, this._animatedValueY ) this.swipeCard(onSwipeDirectionCallback) } else { this.resetTopCard() } if (!this.state.slideGesture) { this.props.onTapCard(this.state.firstCardIndex) } this.setState({ labelType: LABEL_TYPES.NONE, slideGesture: false }) } getOnSwipeDirectionCallback = (animatedValueX, animatedValueY) => { const { onSwipedLeft, onSwipedRight, onSwipedTop, onSwipedBottom } = this.props const { isSwipingLeft, isSwipingRight, isSwipingTop, isSwipingBottom } = this.getSwipeDirection(animatedValueX, animatedValueY) if (isSwipingRight) { return onSwipedRight } if (isSwipingLeft) { return onSwipedLeft } if (isSwipingTop) { return onSwipedTop } if (isSwipingBottom) { return onSwipedBottom } } mustDecrementCardIndex = (animatedValueX, animatedValueY) => { const { isSwipingLeft, isSwipingRight, isSwipingTop, isSwipingBottom } = this.getSwipeDirection(animatedValueX, animatedValueY) return ( (isSwipingLeft && this.props.goBackToPreviousCardOnSwipeLeft) || (isSwipingRight && this.props.goBackToPreviousCardOnSwipeRight) || (isSwipingTop && this.props.goBackToPreviousCardOnSwipeTop) || (isSwipingBottom && this.props.goBackToPreviousCardOnSwipeBottom) ) } getSwipeDirection = (animatedValueX, animatedValueY) => { const isSwipingLeft = animatedValueX < -this.props.horizontalThreshold const isSwipingRight = animatedValueX > this.props.horizontalThreshold const isSwipingTop = animatedValueY < -this.props.verticalThreshold const isSwipingBottom = animatedValueY > this.props.verticalThreshold return { isSwipingLeft, isSwipingRight, isSwipingTop, isSwipingBottom } } resetTopCard = cb => { Animated.spring(this.state.pan, { toValue: 0, friction: this.props.topCardResetAnimationFriction, tension: this.props.topCardResetAnimationTension, useNativeDriver: true }).start(cb) this.state.pan.setOffset({ x: 0, y: 0 }) this.props.onSwipedAborted() } swipeBack = cb => { const { swipeBackXYPositions, isSwipingBack } = this.state const { infinite } = this.props const canSwipeBack = !isSwipingBack && (swipeBackXYPositions.length > 0 || infinite) if (!canSwipeBack) { return } this.setState({isSwipingBack: !isSwipingBack, swipeBackXYPositions}, () => { this.animatePreviousCard(this.calculateNextPreviousCardPosition(), cb) }) } swipeLeft = (mustDecrementCardIndex = false) => { this.swipeCard( this.props.onSwipedLeft, -this.props.horizontalThreshold, 0, mustDecrementCardIndex ) } swipeRight = (mustDecrementCardIndex = false) => { this.swipeCard( this.props.onSwipedRight, this.props.horizontalThreshold, 0, mustDecrementCardIndex ) } swipeTop = (mustDecrementCardIndex = false) => { this.swipeCard( this.props.onSwipedTop, 0, -this.props.verticalThreshold, mustDecrementCardIndex ) } swipeBottom = (mustDecrementCardIndex = false) => { this.swipeCard( this.props.onSwipedBottom, 0, this.props.verticalThreshold, mustDecrementCardIndex ) } swipeCard = ( onSwiped, x = this._animatedValueX, y = this._animatedValueY, mustDecrementCardIndex = false ) => { this.setState({ panResponderLocked: true }) this.animateStack() Animated.timing(this.state.pan, { toValue: { x: x * SWIPE_MULTIPLY_FACTOR, y: y * SWIPE_MULTIPLY_FACTOR }, duration: this.props.swipeAnimationDuration, useNativeDriver: true }).start(() => { this.setSwipeBackCardXY(x, y, () => { mustDecrementCardIndex = mustDecrementCardIndex ? true : this.mustDecrementCardIndex( this._animatedValueX, this._animatedValueY ) if (mustDecrementCardIndex) { this.decrementCardIndex(onSwiped) } else { this.incrementCardIndex(onSwiped) } }) }) } setSwipeBackCardXY = (x = -width, y = 0, cb) => { this.setState({swipeBackXYPositions: [...this.state.swipeBackXYPositions, {x, y}]}, cb) } animatePreviousCard = ({x, y}, cb) => { const { previousCardX, previousCardY } = this.state previousCardX.setValue(x * SWIPE_MULTIPLY_FACTOR) previousCardY.setValue(y * SWIPE_MULTIPLY_FACTOR) Animated.parallel([ Animated.spring(this.state.previousCardX, { toValue: 0, friction: this.props.stackAnimationFriction, tension: this.props.stackAnimationTension, useNativeDriver: true }), Animated.spring(this.state.previousCardY, { toValue: 0, friction: this.props.stackAnimationFriction, tension: this.props.stackAnimationTension, useNativeDriver: true }) ]).start(() => { this.setState({isSwipingBack: false}) this.decrementCardIndex(cb) }) } animateStack = () => { const { secondCardIndex, swipedAllCards } = this.state let { stackSize, infinite, showSecondCard, cards } = this.props let index = secondCardIndex while (stackSize-- > 1 && showSecondCard && !swipedAllCards) { if (this.state[`stackPosition${stackSize}`] && this.state[`stackScale${stackSize}`]) { const newSeparation = this.props.stackSeparation * (stackSize - 1) const newScale = (100 - this.props.stackScale * (stackSize - 1)) * 0.01 Animated.parallel([ Animated.spring(this.state[`stackPosition${stackSize}`], { toValue: newSeparation, friction: this.props.stackAnimationFriction, tension: this.props.stackAnimationTension, useNativeDriver: true }), Animated.spring(this.state[`stackScale${stackSize}`], { toValue: newScale, friction: this.props.stackAnimationFriction, tension: this.props.stackAnimationTension, useNativeDriver: true }) ]).start() } if (index === cards.length - 1) { if (!infinite) break index = 0 } else { index++ } } } incrementCardIndex = onSwiped => { const { firstCardIndex } = this.state const { infinite } = this.props let newCardIndex = firstCardIndex + 1 let swipedAllCards = false this.onSwipedCallbacks(onSwiped) const allSwipedCheck = () => newCardIndex === this.props.cards.length if (allSwipedCheck()) { if (!infinite) { this.props.onSwipedAll() // onSwipeAll may have added cards if (allSwipedCheck()) { swipedAllCards = true } } else { newCardIndex = 0; } } this.setCardIndex(newCardIndex, swipedAllCards) } decrementCardIndex = cb => { const { firstCardIndex } = this.state const lastCardIndex = this.props.cards.length - 1 const previousCardIndex = firstCardIndex - 1 const newCardIndex = firstCardIndex === 0 ? lastCardIndex : previousCardIndex this.onSwipedCallbacks(cb) this.setCardIndex(newCardIndex, false) } jumpToCardIndex = newCardIndex => { if (this.props.cards[newCardIndex]) { this.setCardIndex(newCardIndex, false) } } rebuildStackValues = () => { const stackPositionsAndScales = {} const { stackSize, stackSeparation, stackScale } = this.props for (let position = 0; position < stackSize; position++) { stackPositionsAndScales[`stackPosition${position}`] = new Animated.Value(stackSeparation * position) stackPositionsAndScales[`stackScale${position}`] = new Animated.Value((100 - stackScale * position) * 0.01) } return stackPositionsAndScales } onSwipedCallbacks = (swipeDirectionCallback) => { const previousCardIndex = this.state.firstCardIndex this.props.onSwiped(previousCardIndex, this.props.cards[previousCardIndex]) this.setState(this.rebuildStackValues) if (swipeDirectionCallback) { swipeDirectionCallback(previousCardIndex, this.props.cards[previousCardIndex]) } } setCardIndex = (newCardIndex, swipedAllCards) => { if (this._mounted) { this.setState( { ...calculateCardIndexes(newCardIndex, this.props.cards), swipedAllCards: swipedAllCards, panResponderLocked: false }, this.resetPanAndScale ) } } resetPanAndScale = () => { const {previousCardDefaultPositionX, previousCardDefaultPositionY} = this.props this.state.pan.setValue({ x: 0, y: 0 }) this.state.pan.setOffset({ x: 0, y: 0}) this._animatedValueX = 0 this._animatedValueY = 0 this.state.previousCardX.setValue(previousCardDefaultPositionX) this.state.previousCardY.setValue(previousCardDefaultPositionY) this.state.pan.x.addListener(value => this._animatedValueX = value.value) this.state.pan.y.addListener(value => this._animatedValueY = value.value) } calculateNextPreviousCardPosition = () => { const { swipeBackXYPositions } = this.state let { previousCardDefaultPositionX: x, previousCardDefaultPositionY: y } = this.props const swipeBackPosition = swipeBackXYPositions.splice(-1, 1) if (swipeBackPosition[0]) { x = swipeBackPosition[0].x y = swipeBackPosition[0].y } return { x, y } } calculateOverlayLabelStyle = () => { const dynamicStyle = this.props.overlayLabels[this.state.labelType].style let overlayLabelStyle = dynamicStyle ? dynamicStyle.label : {} if (this.state.labelType === LABEL_TYPES.NONE) { overlayLabelStyle = styles.hideOverlayLabel } return [this.props.overlayLabelStyle, overlayLabelStyle] } calculateOverlayLabelWrapperStyle = () => { const dynamicStyle = this.props.overlayLabels[this.state.labelType].style const dynamicWrapperStyle = dynamicStyle ? dynamicStyle.wrapper : {} const opacity = this.props.animateOverlayLabelsOpacity ? this.interpolateOverlayLabelsOpacity() : 1 return [this.props.overlayLabelWrapperStyle, dynamicWrapperStyle, { opacity }] } calculateSwipableCardStyle = () => { const opacity = this.props.animateCardOpacity ? this.interpolateCardOpacity() : 1 const rotation = this.interpolateRotation() return [ styles.card, this.getCardStyle(), { zIndex: 1, opacity: opacity, transform: [ { translateX: this.state.pan.x }, { translateY: this.state.pan.y }, { rotate: rotation } ] }, this.props.cardStyle ] } calculateStackCardZoomStyle = (position) => [ styles.card, this.getCardStyle(), { zIndex: position * -1, transform: [{ scale: this.state[`stackScale${position}`] }, { translateY: this.state[`stackPosition${position}`] }] }, this.props.cardStyle ] calculateSwipeBackCardStyle = () => [ styles.card, this.getCardStyle(), { zIndex: 4, transform: [ { translateX: this.state.previousCardX }, { translateY: this.state.previousCardY } ] }, this.props.cardStyle ] interpolateCardOpacity = () => { const animatedValueX = Math.abs(this._animatedValueX) const animatedValueY = Math.abs(this._animatedValueY) let opacity if (animatedValueX > animatedValueY) { opacity = this.state.pan.x.interpolate({ inputRange: this.props.inputCardOpacityRangeX, outputRange: this.props.outputCardOpacityRangeX }) } else { opacity = this.state.pan.y.interpolate({ inputRange: this.props.inputCardOpacityRangeY, outputRange: this.props.outputCardOpacityRangeY }) } return opacity } interpolateOverlayLabelsOpacity = () => { const animatedValueX = Math.abs(this._animatedValueX) const animatedValueY = Math.abs(this._animatedValueY) let opacity if (animatedValueX > animatedValueY) { opacity = this.state.pan.x.interpolate({ inputRange: this.props.inputOverlayLabelsOpacityRangeX, outputRange: this.props.outputOverlayLabelsOpacityRangeX }) } else { opacity = this.state.pan.y.interpolate({ inputRange: this.props.inputOverlayLabelsOpacityRangeY, outputRange: this.props.outputOverlayLabelsOpacityRangeY }) } return opacity } interpolateRotation = () => this.state.pan.x.interpolate({ inputRange: this.props.inputRotationRange, outputRange: this.props.outputRotationRange }) render = () => { const { pointerEvents, backgroundColor, marginTop, marginBottom, containerStyle, swipeBackCard, testID } = this.props return ( <View pointerEvents={pointerEvents} testID={testID} style={[ styles.container, { backgroundColor: backgroundColor, marginTop: marginTop, marginBottom: marginBottom }, containerStyle ]} > {this.renderChildren()} {swipeBackCard ? this.renderSwipeBackCard() : null} {this.renderStack()} </View> ) } renderChildren = () => { const { childrenOnTop, children, stackSize, showSecondCard } = this.props let zIndex = (stackSize && showSecondCard) ? stackSize * -1 : 1 if (childrenOnTop) { zIndex = 5 } return ( <View pointerEvents='box-none' style={[styles.childrenViewStyle, { zIndex: zIndex }]}> {children} </View> ) } getCardKey = (cardContent, cardIndex) => { const { keyExtractor } = this.props if (keyExtractor) { return keyExtractor(cardContent) } return cardIndex } pushCardToStack = (renderedCards, index, position, key, firstCard) => { const { cards } = this.props const stackCardZoomStyle = this.calculateStackCardZoomStyle(position) const stackCard = this.props.renderCard(cards[index], index) const swipableCardStyle = this.calculateSwipableCardStyle() const renderOverlayLabel = this.renderOverlayLabel() renderedCards.push( <Animated.View key={key} style={firstCard ? swipableCardStyle : stackCardZoomStyle} {...this._panResponder.panHandlers} > {firstCard ? renderOverlayLabel : null} {stackCard} </Animated.View> ) } renderStack = () => { const { firstCardIndex, swipedAllCards } = this.state const { cards } = this.props const renderedCards = [] let { stackSize, infinite, showSecondCard } = this.props let index = firstCardIndex let firstCard = true let cardPosition = 0 while (stackSize-- > 0 && (firstCard || showSecondCard) && !swipedAllCards) { const key = this.getCardKey(cards[index], index) this.pushCardToStack(renderedCards, index, cardPosition, key, firstCard) firstCard = false if (index === cards.length - 1) { if (!infinite) break index = 0 } else { index++ } cardPosition++ } return renderedCards } renderSwipeBackCard = () => { const { previousCardIndex } = this.state const { cards } = this.props const previousCardStyle = this.calculateSwipeBackCardStyle() const previousCard = this.props.renderCard(cards[previousCardIndex], previousCardIndex) const key = this.getCardKey(cards[previousCardIndex], previousCardIndex) return ( <Animated.View key={key} style={previousCardStyle}> {previousCard} </Animated.View> ) } renderOverlayLabel = () => { const { disableBottomSwipe, disableLeftSwipe, disableRightSwipe, disableTopSwipe, overlayLabels } = this.props const { labelType } = this.state const labelTypeNone = labelType === LABEL_TYPES.NONE const directionSwipeLabelDisabled = (labelType === LABEL_TYPES.BOTTOM && disableBottomSwipe) || (labelType === LABEL_TYPES.LEFT && disableLeftSwipe) || (labelType === LABEL_TYPES.RIGHT && disableRightSwipe) || (labelType === LABEL_TYPES.TOP && disableTopSwipe) if ( !overlayLabels || !overlayLabels[labelType] || labelTypeNone || directionSwipeLabelDisabled ) { return null } return ( <Animated.View style={this.calculateOverlayLabelWrapperStyle()}> {!overlayLabels[labelType].element && <Text style={this.calculateOverlayLabelStyle()}> {overlayLabels[labelType].title} </Text> } {overlayLabels[labelType].element && overlayLabels[labelType].element } </Animated.View> ) } } Swiper.propTypes = { animateCardOpacity: PropTypes.bool, animateOverlayLabelsOpacity: PropTypes.bool, backgroundColor: PropTypes.string, cardHorizontalMargin: PropTypes.number, cardIndex: PropTypes.number, cardStyle: PropTypes.oneOfType([PropTypes.number, PropTypes.object]), cardVerticalMargin: PropTypes.number, cards: PropTypes.oneOfType([PropTypes.array, PropTypes.object]).isRequired, containerStyle: PropTypes.object, children: PropTypes.any, childrenOnTop: PropTypes.bool, dragEnd: PropTypes.func, dragStart: PropTypes.func, disableBottomSwipe: PropTypes.bool, disableLeftSwipe: PropTypes.bool, disableRightSwipe: PropTypes.bool, disableTopSwipe: PropTypes.bool, goBackToPreviousCardOnSwipeBottom: PropTypes.bool, goBackToPreviousCardOnSwipeLeft: PropTypes.bool, goBackToPreviousCardOnSwipeRight: PropTypes.bool, goBackToPreviousCardOnSwipeTop: PropTypes.bool, horizontalSwipe: PropTypes.bool, horizontalThreshold: PropTypes.number, infinite: PropTypes.bool, inputCardOpacityRangeX: PropTypes.array, inputCardOpacityRangeY: PropTypes.array, inputOverlayLabelsOpacityRangeX: PropTypes.array, inputOverlayLabelsOpacityRangeY: PropTypes.array, inputCardOpacityRange: PropTypes.array, inputRotationRange: PropTypes.array, keyExtractor: PropTypes.func, marginBottom: PropTypes.number, marginTop: PropTypes.number, onSwiped: PropTypes.func, onSwipedAborted: PropTypes.func, onSwipedAll: PropTypes.func, onSwipedBottom: PropTypes.func, onSwipedLeft: PropTypes.func, onSwipedRight: PropTypes.func, onSwipedTop: PropTypes.func, onSwiping: PropTypes.func, onTapCard: PropTypes.func, onTapCardDeadZone: PropTypes.number, outputCardOpacityRangeX: PropTypes.array, outputCardOpacityRangeY: PropTypes.array, outputOverlayLabelsOpacityRangeX: PropTypes.array, outputOverlayLabelsOpacityRangeY: PropTypes.array, outputRotationRange: PropTypes.array, outputCardOpacityRange: PropTypes.array, overlayLabels: PropTypes.object, overlayLabelStyle: PropTypes.object, overlayLabelWrapperStyle: PropTypes.object, overlayOpacityHorizontalThreshold: PropTypes.number, overlayOpacityVerticalThreshold: PropTypes.number, pointerEvents: PropTypes.oneOf(['box-none', 'none', 'box-only', 'auto']), previousCardDefaultPositionX: PropTypes.number, previousCardDefaultPositionY: PropTypes.number, renderCard: PropTypes.func.isRequired, secondCardZoom: PropTypes.number, showSecondCard: PropTypes.bool, stackAnimationFriction: PropTypes.number, stackAnimationTension: PropTypes.number, stackScale: PropTypes.number, stackSeparation: PropTypes.number, stackSize: PropTypes.number, swipeAnimationDuration: PropTypes.number, swipeBackCard: PropTypes.bool, testID: PropTypes.string, topCardResetAnimationFriction: PropTypes.number, topCardResetAnimationTension: PropTypes.number, verticalSwipe: PropTypes.bool, verticalThreshold: PropTypes.number, zoomAnimationDuration: PropTypes.number, zoomFriction: PropTypes.number } Swiper.defaultProps = { animateCardOpacity: false, animateOverlayLabelsOpacity: false, backgroundColor: '#4FD0E9', cardHorizontalMargin: 20, cardIndex: 0, cardStyle: {}, cardVerticalMargin: 60, childrenOnTop: false, containerStyle: {}, disableBottomSwipe: false, disableLeftSwipe: false, disableRightSwipe: false, disableTopSwipe: false, horizontalSwipe: true, horizontalThreshold: width / 4, goBackToPreviousCardOnSwipeBottom: false, goBackToPreviousCardOnSwipeLeft: false, goBackToPreviousCardOnSwipeRight: false, goBackToPreviousCardOnSwipeTop: false, infinite: false, inputCardOpacityRangeX: [-width / 2, -width / 3, 0, width / 3, width / 2], inputCardOpacityRangeY: [-height / 2, -height / 3, 0, height / 3, height / 2], inputOverlayLabelsOpacityRangeX: [ -width / 3, -width / 4, 0, width / 4, width / 3 ], inputOverlayLabelsOpacityRangeY: [ -height / 4, -height / 5, 0, height / 5, height / 4 ], inputRotationRange: [-width / 2, 0, width / 2], keyExtractor: null, marginBottom: 0, marginTop: 0, onSwiped: cardIndex => { }, onSwipedAborted: () => { }, onSwipedAll: () => { }, onSwipedBottom: cardIndex => { }, onSwipedLeft: cardIndex => { }, onSwipedRight: cardIndex => { }, onSwipedTop: cardIndex => { }, onSwiping: () => { }, onTapCard: (cardIndex) => { }, onTapCardDeadZone: 5, outputCardOpacityRangeX: [0.8, 1, 1, 1, 0.8], outputCardOpacityRangeY: [0.8, 1, 1, 1, 0.8], outputOverlayLabelsOpacityRangeX: [1, 0, 0, 0, 1], outputOverlayLabelsOpacityRangeY: [1, 0, 0, 0, 1], outputRotationRange: ['-10deg', '0deg', '10deg'], overlayLabels: null, overlayLabelStyle: { fontSize: 45, fontWeight: 'bold', borderRadius: 10, padding: 10, overflow: 'hidden' }, overlayLabelWrapperStyle: { position: 'absolute', backgroundColor: 'transparent', zIndex: 2, flex: 1, width: '100%', height: '100%' }, overlayOpacityHorizontalThreshold: width / 4, overlayOpacityVerticalThreshold: height / 5, pointerEvents: 'auto', previousCardDefaultPositionX: -width, previousCardDefaultPositionY: -height, secondCardZoom: 0.97, showSecondCard: true, stackAnimationFriction: 7, stackAnimationTension: 40, stackScale: 3, stackSeparation: 10, stackSize: 1, swipeAnimationDuration: 350, swipeBackCard: false, topCardResetAnimationFriction: 7, topCardResetAnimationTension: 40, verticalSwipe: true, verticalThreshold: height / 5, zoomAnimationDuration: 100, zoomFriction: 7 } export default Swiper