Closed guillermodlpa closed 6 years ago
Do you have any ideal on how to do this ? I would like the dimensions to be calculated like this too, but I haven't found a good way to do it yet. You can use the margin props to achieve your desired effect. Any ideals @gpuenteallott
Hi @alexbrillant thanks for replying! :)
I tried to fork and play with it locally, but I wasn't able to kick off the example in my simulator. Apart from the error I was getting, I noticed that the package.json
version specified is ^1.2.1
but in npm the latest published is 1.2.0
.
To determine width/height, there's the onLayout
way, and the measure
way. I've never tried measure
myself though. Did you try them?
Also, googling around I found another react native swiper that doesn't use card dimensions in its calculations. It's interesting to look at that code. It's using a hardcoded number for the swipe threshold.
@gpuenteallott I was curious about this also. That cardHeight
variable inside Swiper.js is being used inside the cardStyle
object. But just about everywhere cardStyle
is being used, there is a corresponding this.customCardStyle
. For example, lines 517 to 537:
calculateSwipableCardStyle = () => {
const opacity = this.props.animateCardOpacity
? this.interpolateCardOpacity()
: 1
const rotation = this.interpolateRotation()
return [
styles.card,
this.cardStyle,
{
zIndex: 3,
opacity: opacity,
transform: [
{ translateX: this.state.pan.x },
{ translateY: this.state.pan.y },
{ rotate: rotation }
]
},
this.customCardStyle
]
}
customCardStyle
is initialized inside initializeCardStyle
and is assigned the cardStyle
prop:
this.customCardStyle = this.props.cardStyle
I'm using react-native-deck-swiper
npm version 1.3.5
In a fork I am using locally, i added width and height as a proptype specific to the card itself. If it is present it uses that, otherwise it defaults to the library default with some modifications. I am also adding a division modifier proptype to control how tall the cards are because I was having problems getting consistent sizing behavior. Overall this feels much more stable and I'm considering cleaning it up for a PR that would add a ton of control to width/height and allow you to properly use flex to style inside the card container without worrying about overflow being cut off by the old margin (and be consistent across devices of different sizes - because the current margin controls can cut off the card on smaller devices because of the naive device width/height calcs that are absolute)
@alexbrillant excerpt my modifications that I may clean up and put in a PR if you are interested in checking it out (divisors default to 1, thus no change if nothing given). It largely preserves the old behavior:
Note: this means the tap bounds will be exact to the card, not floating. This means you can set top
, left
, bottom
, right
on an image inside the Card and flex the heck out of it and have the entire card actually match the tap bounds.
initializeCardStyle = () => {
//cardInputWidth: PropTypes.number,
//cardInputHeight: PropTypes.number,
const {
cardVerticalMargin,
cardHorizontalMargin,
cardInputWidth,
cardInputHeight,
heightDivisor,
widthDivisor,
marginTop,
marginBottom
} = this.props
var cardWidthTemp = 0;
var cardHeightTemp = 0;
if (!cardInputWidth) {
cardWidthTemp = (width - cardHorizontalMargin * 2) / widthDivisor;
} else {
cardWidthTemp = cardInputWidth;
}
if (!cardInputHeight) {
cardHeightTemp = (height - cardVerticalMargin * 2 - marginTop - marginBottom) / heightDivisor; //1.3
} else {
cardHeightTemp = cardInputHeight;
}
const cardWidth = cardWidthTemp;
const cardHeight = cardHeightTemp;
this.cardStyle = {
top: cardVerticalMargin,
alignSelf: 'center',
width: cardWidth,
height: cardHeight
}
this.customCardStyle = this.props.cardStyle
}
Example usage:
renderCard(cardObject) {
return (
<View style={styles.cardWrapper}>
<View style={{flex: 2}}>
<Image source={{ uri: cardObject.ImageThatFillsView }} style={styles.cardImage} />
</View>
<View style={styles.cardSpacer}>
<Text style={styles.profileName}>{cardObject.name}</Text>
<View style={styles.descriptionContainer}>
<Text style={styles.sectionHeader}>Some text</Text>
<Text style={styles.profileText}>{cardObject.someText}</Text>
</View>
</View>
</View>
)
}
render() {
return (
<View style={styles.container}>
<Swiper
cards={this.state.data}
renderCard={this.renderCard}
cardStyle={styles.swiperStyle}
onSwiped={(cardIndex) => {console.log(cardIndex)}}
onSwipedAll={() => {console.log('onSwipedAll')}}
cardIndex={0}
cardInputWidth={300}
onTapCard={(cardIndex) => {alert("Tapped card.")}}
onTapCardDeadZone={3}
backgroundColor={GLOBAL.COLOR.LIGHT_PURPLE}>
<Text style={styles.helpText}> Swipe up or down to continue to the next card. </Text>
<Text style={styles.helpText}> Tap the picture to view. </Text>
</Swiper>
</View>
)
... style
swiperStyle: {
flex: 1,
borderWidth: 5,
borderRadius: 10
},
cardImage: {
position: 'absolute',
alignSelf: 'center',
top: 0,
left: 0,
bottom: 0,
right: 0,
resizeMode: 'cover',
},
Looks like:
tl;dr It's not quite what the original ask was for, but I believe it is a good compromise.
Edit edit edit edit: I may be able to utilize this to figure out the "stack" offset as well thats been requested.
Why not using just CSS? Am I missing something that will be broken?
I am using the following, seems to be working just fine.
<Swiper
ref={swiper => { this.swiper = swiper }}
cards={questions}
verticalSwipe={false}
onSwipedLeft={this.handleSwipedLeft}
onSwipedRight={this.handleSwipedRight}
onSwipedAll={this.handleFinish}
showSecondCard={questions.length > 1}
backgroundColor={'transparent'}
cardStyle={{
top: 0,
left: 0,
bottom: 0,
right: 0,
width: 'auto',
height: 'auto'
}}
renderCard={card => (
<CardDetails
card={card}
handleAnswerToggle={this.handleAnswerToggle}
/>
)}
/>
A had an issue similar to this one. In my case I wanted the card to render above all the other content on the screen when dragged around. I was able make the parent Views width and height overlap the area I wanted the card to overlap. From there I used @felipedeboni approach to adjust the cardStyle.
There was a hidden view component in the swiper that was stopping my click events from getting through to content underneath. To fix it, I did have to go inside the Swiper code and change the render method to include pointerEvents="box-none".
render = () => {
return (
<View
pointerEvents="box-none"
style={[
styles.container,
{
backgroundColor: this.props.backgroundColor,
marginTop: this.props.marginTop,
marginBottom: this.props.marginBottom
}
]}
>
{this.renderChildren()}
{this.renderStack()}
{this.props.swipeBackCard ? this.renderSwipeBackCard() : null}
</View>
);
};
Perhaps this can be changed in the repository? It is one line pointerEvents="box-none" for the view being rendered. @alexbrillant what do you think?
@bernhardt1 @alexbrillant simply adding the prop wouldn't be good as it wouldn't be configurable. Having it as a swiper prop with a default value could actually work.
Problem
Card height/width are currently calculated from window dimensions.
[...]
This works well for a full screen deck swiper. However, it's not correct if the deck swiper is used inside a container with other dimensions. Cards currently will overflow and most likely render partially outside of the screen. My specific use case is having a header and a deck container that grows to fill remaining screen size.
Solution
I suggest updating implementation so that card dimensions are calculated from the Swiper component dimensions, instead of the window.
Thoughts?