Skip to content

Commit

Permalink
Add FlipCard example
Browse files Browse the repository at this point in the history
  • Loading branch information
exploIF committed Apr 16, 2024
1 parent 578e17b commit f8e73f3
Show file tree
Hide file tree
Showing 4 changed files with 192 additions and 0 deletions.
32 changes: 32 additions & 0 deletions docs/blog/flip-card.md
@@ -0,0 +1,32 @@
---
slug: flipCard
title: Flip Card
---

Flip card component is a component that 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={[111,111]} />

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={[56,58]} />

<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.

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

160 changes: 160 additions & 0 deletions docs/static/examples/FlipCard.js
@@ -0,0 +1,160 @@
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>Regular content ✨</Text>
</View>
);
};

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

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

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

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: 150,
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 f8e73f3

Please sign in to comment.