Skip to content

Commit

Permalink
Add FlipCard example (#5905)
Browse files Browse the repository at this point in the history
## Summary

Added FlipCard component to our examples

---------

Co-authored-by: Miron Markowski <majron15@gmail.com>
Co-authored-by: Patrycja Kalińska <cysiak0412@gmail.com>
Co-authored-by: kacperkapusciak <kacper.kapusciak@swmansion.com>
  • Loading branch information
4 people committed May 23, 2024
1 parent e3387a9 commit e661676
Show file tree
Hide file tree
Showing 4 changed files with 200 additions and 0 deletions.
34 changes: 34 additions & 0 deletions docs/blog/flip-card.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
---
slug: flipCard
title: Flip Card
---

Flip card allows you to display different content depending on whether the card is flipped or not. It can be especially useful when you do not want to display some data immediately after entering the screen (e.g. secure data) and only after fulfilling a certain condition or performing an action.

import FlipCard from '@site/static/examples/FlipCard';
import FlipCardSrc from '!!raw-loader!@site/static/examples/FlipCard';
import ExampleVideo from '@site/src/components/ExampleVideo';

<InteractiveExample src={FlipCardSrc} component={<FlipCard />} />

For storing information about whether the card is flipped or not we use [shared value](/docs/fundamentals/glossary#shared-value) with the `useSharedValue` hook. Using shared values helps to prevent unnecessary re-renders.

<CollapsibleCode src={FlipCardSrc} showLines={[117,117]} />

This allows us to [interpolate](/docs/utilities/interpolate) values between 0-180 and 180-360 degrees, depending on whether the card is flipped or not. In addition, we use [withTiming](/docs/animations/withTiming) util which makes our animation smooth.

<CollapsibleCode src={FlipCardSrc} showLines={[62,64]} />

<ExampleVideo
sources={{
android: "/react-native-reanimated/recordings/examples/flip_card_android.mov",
ios: "/react-native-reanimated/recordings/examples/flip_card_ios.mov"
}}
/>

The **FlipCard** component accepts several props: `duration` allows you to change the duration of the animation, setting `direction` to the value `x` allows you to change the direction of our animation, **RegularContent** and **FlippedContent** give ability to display different content for flipped and non flipped variants.

<samp id="FlipCard">Flip Card</samp>

<CollapsibleCode src={FlipCardSrc} showLines={[51,103]} />

166 changes: 166 additions & 0 deletions docs/static/examples/FlipCard.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
import React from 'react';
import { Pressable, SafeAreaView, View, StyleSheet, Text } from 'react-native';
import Animated, {
interpolate,
useAnimatedStyle,
useSharedValue,
withTiming,
} from 'react-native-reanimated';

const RegularContent = () => {
return (
<View style={regularContentStyles.card}>
<Text style={regularContentStyles.text}>Regular content ✨</Text>
</View>
);
};

const regularContentStyles = StyleSheet.create({
card: {
flex: 1,
backgroundColor: '#b6cff7',
borderRadius: 16,
justifyContent: 'center',
alignItems: 'center',
},
text: {
color: '#001a72',
},
});

const FlippedContent = () => {
return (
<View style={flippedContentStyles.card}>
<Text style={flippedContentStyles.text}>Flipped content 🚀</Text>
</View>
);
};

const flippedContentStyles = StyleSheet.create({
card: {
flex: 1,
backgroundColor: '#baeee5',
borderRadius: 16,
justifyContent: 'center',
alignItems: 'center',
},
text: {
color: '#001a72',
},
});

const FlipCard = ({
isFlipped,
cardStyle,
direction = 'y',
duration = 500,
RegularContent,
FlippedContent,
}) => {
const isDirectionX = direction === 'x';

const regularCardAnimatedStyle = useAnimatedStyle(() => {
const spinValue = interpolate(Number(isFlipped.value), [0, 1], [0, 180]);
const rotateValue = withTiming(`${spinValue}deg`, { duration });

return {
transform: [
isDirectionX ? { rotateX: rotateValue } : { rotateY: rotateValue },
],
};
});

const flippedCardAnimatedStyle = useAnimatedStyle(() => {
const spinValue = interpolate(Number(isFlipped.value), [0, 1], [180, 360]);
const rotateValue = withTiming(`${spinValue}deg`, { duration });

return {
transform: [
isDirectionX ? { rotateX: rotateValue } : { rotateY: rotateValue },
],
};
});

return (
<View>
<Animated.View
style={[
flipCardStyles.regularCard,
cardStyle,
regularCardAnimatedStyle,
]}>
{RegularContent}
</Animated.View>
<Animated.View
style={[
flipCardStyles.flippedCard,
cardStyle,
flippedCardAnimatedStyle,
]}>
{FlippedContent}
</Animated.View>
</View>
);
};

const flipCardStyles = StyleSheet.create({
regularCard: {
position: 'absolute',
zIndex: 1,
},
flippedCard: {
backfaceVisibility: 'hidden',
zIndex: 2,
},
});

export default function App() {
const isFlipped = useSharedValue(false);

const handlePress = () => {
isFlipped.value = !isFlipped.value;
};

return (
<SafeAreaView style={styles.container}>
<FlipCard
isFlipped={isFlipped}
cardStyle={styles.flipCard}
FlippedContent={<FlippedContent />}
RegularContent={<RegularContent />}
/>
<View style={styles.buttonContainer}>
<Pressable style={styles.toggleButton} onPress={handlePress}>
<Text style={styles.toggleButtonText}>Toggle card</Text>
</Pressable>
</View>
</SafeAreaView>
);
}

const styles = StyleSheet.create({
container: {
flex: 1,
height: 300,
alignItems: 'center',
justifyContent: 'center',
},
buttonContainer: {
marginTop: 16,
justifyContent: 'center',
alignItems: 'center',
},
toggleButton: {
backgroundColor: '#b58df1',
padding: 12,
borderRadius: 48,
},
toggleButtonText: {
color: '#fff',
textAlign: 'center',
},
flipCard: {
width: 170,
height: 200,
},
});
Binary file not shown.
Binary file added docs/static/recordings/examples/flip_card_ios.mov
Binary file not shown.

0 comments on commit e661676

Please sign in to comment.