简体   繁体   中英

react native: rendering cards that flip on user tap

I'm working on a React Native code that renders “cards” on an array using a map function. Each card is wrapped in a touchableOpacity component, so that when a user taps the card, it would flip. Currently the issue is that, if a user taps to flip one card, all neighboring cards flip as well. I would like to have the flip functionality for each card be independent. When a card is flipped it should not trigger the flipping of neighboring cards as well. Thanks in advance to reading this.

class SavedBooks extends Component {
    componentWillMount() {
        this.animatedValue = new Animated.Value(0);
        this.value = 0;
        this.animatedValue.addListener(({ value }) => { this.value = value })
    }

    frontCardStyle() {
        this.frontInterpolate = this.animatedValue.interpolate({
            inputRange: [0, 180],
            outputRange: ['0deg', '180deg']
        })

        const frontAnimatedStyle = {
            transform: [ { rotateY: this.frontInterpolate }]
        }

        return frontAnimatedStyle
    }

    backCardStyle() {
        this.backInterpolate = this.animatedValue.interpolate({
            inputRange: [0, 180],
            outputRange: ['180deg', '360deg']
        })

        const backAnimatedStyle = { transform: [{ rotateY: this.backInterpolate }] }
        return backAnimatedStyle
    }

    flipCard() {
        if (this.value >= 90) {
            Animated.spring(this.animatedValue, {
                toValue: 0,
                friction: 8,
                tension: 10
            }).start();
        } else if (this.value < 90) {
            Animated.spring(this.animatedValue, {
                toValue: 180,
                friction: 8,
                tension: 10
            }).start();
        }
    }


    renderElemets(color) {
        const { savedBooks } = this.props.book 
        return savedBooks.map((book, index) => {
            return (
            <View
                key={index}
                style={{ alignItems: 'center' }}>

                <TouchableOpacity onPress={() => this.flipCard()} >
                    <Text
                        style={{ fontFamily: 'Helvetica', 
                        fontSize: 25, 
                        padding: 15 }}>
                        {book.title}
                    </Text>
                    <Animated.View>
                        <Animated.Image
                            style={[this.frontCardStyle(), styles.cardStyle]}
                            source={{ uri: book.image }}
                        />
                            <Animated.View style={[this.backCardStyle(), styles.cardStyle, styles.flipCardBack]}>
                            <Text>{book.description}</Text>
                        </Animated.View>
                    </Animated.View>
                </TouchableOpacity>
            </View>
            )
        });
    }

    render() {
        return (
            <ScrollView>
                {this.renderElemets(color)}
            </ScrollView>
            );
        }
}



const styles = StyleSheet.create({
    container: {
        flex: 1,
        backgroundColor: '#FFF',
    },
    imageStyle: {
        position: 'absolute',
        top: 0,
        left: 0,
        bottom: 0,
        right: 0,
    },
    cardStyle: {
        height: 400,
        width: 250,
        backfaceVisibility: 'hidden',
    },
    flipCardBack: {
        position: "absolute",
        top: 0,
    },
});

Because all the cards share the same style. You should create a card component so each card can have their own style and the press event won't affect other cards.

I had this exact problem and solved it by adding something like an identifier or tag to the clicked item in the array. The idea is quite similar to adding a class 'active' to the item clicked in the array, that way your flipCard() function will only be run on the item which has been labelled active. In short, only add the frontCardStyle() or backCardStyle() to the item that has been clicked and labelled 'active'. That way only the active item will flip. I followed this example and came up with the solution below;

 constructor(props) { super(props); this.state = { activeItem: {}, } this.toggleActiveItem = this.toggleActiveItem.bind(this); } toggleActiveItem(index) { this.setState({ activeItem: { [index]: true } }); this.flipCard(); } renderElemets(color) { const { savedBooks } = this.props.book return savedBooks.map((book, index) => { return ( <View key={index} style={{ alignItems: 'center' }}> <TouchableOpacity onPress={() => this.toggleActiveItem(index)} > <Text style={{ fontFamily: 'Helvetica', fontSize: 25, padding: 15 }}> {book.title} </Text> <Animated.View> <Animated.Image style={[this.state.activeItem[index] && this.frontCardStyle(), styles.cardStyle]} source={{ uri: book.image }} /> <Animated.View style={[this.state.activeItem[index] && this.backCardStyle(), styles.cardStyle, styles.flipCardBack]}> <Text>{book.description}</Text> </Animated.View> </Animated.View> </TouchableOpacity> </View> ) }); } 

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM