Qr Code
A tap to expand qr view that smoothly morphs
Last updated on
Manual
Install the following dependencies:
npm install react-native-reanimated expo-blur @expo/vector-icons react-native-qrcode-styledCopy and paste the following code into your project.
component/base/qr-code.tsx
import { Pressable, StyleSheet, Text, type ViewStyle } from "react-native";import React, { memo } from "react";import Animated, { interpolate, interpolateColor, useAnimatedProps, useAnimatedStyle, useSharedValue, withSpring,} from "react-native-reanimated";import { Feather, Ionicons } from "@expo/vector-icons";import QRCodeStyled from "react-native-qrcode-styled";import { BlurView, type BlurViewProps } from "expo-blur";import type { QRCodeProps } from "./types";import { BACKGROUND_COLOR, QR_URL, SPRING_CONFIG } from "./const";const AnimatedQRCodeStyled = Animated.createAnimatedComponent(QRCodeStyled);const AnimatedBlurView = Animated.createAnimatedComponent<BlurViewProps>(BlurView);const QRCode: React.FC<QRCodeProps> & React.FunctionComponent<QRCodeProps> = memo<QRCodeProps>( ({ QRCodevalue, springConfig = SPRING_CONFIG, textStyle, backgroundColorFocused = BACKGROUND_COLOR, }: QRCodeProps): | (React.ReactNode & React.JSX.Element & React.ReactElement) | null => { const progress = useSharedValue<number>(0); const containerStylez = useAnimatedStyle< Pick<ViewStyle, "width" | "height" | "borderRadius" | "backgroundColor"> >(() => { return { width: interpolate(progress.value, [0, 1], [200, 250]), height: interpolate(progress.value, [0, 1], [50, 320]), borderRadius: interpolate(progress.value, [0, 1], [100, 25]), backgroundColor: interpolateColor( progress.value, [0, 1], [BACKGROUND_COLOR, backgroundColorFocused], ), }; }); const textContainerStyle = useAnimatedStyle<Pick<ViewStyle, "opacity">>( () => { return { opacity: interpolate(progress.value, [0, 1], [1, 0]), }; }, ); const qrCodeStylez = useAnimatedStyle< Pick<ViewStyle, "opacity" | "transform"> >(() => { const translateY = interpolate(progress.value, [0, 1], [0, -35]); return { opacity: interpolate(progress.value, [0, 1], [0, 1]), transform: [{ translateY }], }; }); const buttonsStyle = useAnimatedStyle< Pick<ViewStyle, "opacity" | "transform"> >(() => { const translateY = interpolate(progress.value, [0, 1], [0, 10]); return { opacity: interpolate(progress.value, [0, 1], [0, 1]), transform: [{ translateY }], }; }); const onPress = () => { if (progress.value === 1) { progress.value = withSpring<number>(0, springConfig); return; } progress.value = withSpring<number>(1, { ...springConfig, }); }; const handleCopy = async () => {}; const handleClose = () => { progress.value = withSpring<number>(0, { ...springConfig, }); }; const animatedBlurViewPropz = useAnimatedProps< Pick<BlurViewProps, "intensity"> >(() => { const intensity = withSpring<number>( interpolate(progress.value, [0, 1], [20, 1]), ); return { intensity, }; }); const animatedQRStylez = useAnimatedStyle< Pick<ViewStyle, "borderRadius" | "backgroundColor"> >(() => { return { borderRadius: interpolate(progress.value, [0, 1], [100, 30]), backgroundColor: interpolateColor( progress.value, [0, 1], [BACKGROUND_COLOR, backgroundColorFocused], ), }; }); const animatedBlurViewPropsBottom = useAnimatedProps< Pick<BlurViewProps, "intensity"> >(() => { const intensity = withSpring<number>( interpolate(progress.value, [0, 0.5, 1], [0, 50, 0]), ); return { intensity, }; }); return ( <Pressable onPress={onPress}> <Animated.View style={[styles.container, containerStylez]}> <Animated.View style={[styles.textContainer, textContainerStyle]}> <Ionicons name="qr-code-outline" size={24} color="black" /> <Text style={[styles.text, textStyle]}>Show QR Code</Text> </Animated.View> <Animated.View style={[styles.qrContainer, qrCodeStylez]}> <AnimatedQRCodeStyled data={QRCodevalue ?? QR_URL} // style={{ borderRadius: 30, backgroundColor: "red" }} style={animatedQRStylez} padding={20} size={190} /> <AnimatedBlurView tint="systemChromeMaterialLight" animatedProps={[animatedBlurViewPropz]} style={[ StyleSheet.absoluteFillObject, { overflow: "hidden", borderRadius: 30, }, ]} /> </Animated.View> <Animated.View style={[styles.buttonsContainer, buttonsStyle]}> <Pressable style={styles.button} onPress={handleCopy}> <Feather name="copy" size={20} color="black" /> <Text style={[styles.buttonText, textStyle]}>Copy Link</Text> </Pressable> <Pressable style={styles.closeButton} onPress={handleClose}> <Feather name="x" size={20} color="black" /> </Pressable> </Animated.View> <AnimatedBlurView pointerEvents={"none"} tint="systemUltraThinMaterialLight" animatedProps={animatedBlurViewPropsBottom} style={[ StyleSheet.absoluteFillObject, { overflow: "hidden", borderRadius: 30, }, ]} /> </Animated.View> </Pressable> ); }, );export default memo< React.FC<QRCodeProps> & React.FunctionComponent<QRCodeProps>>(QRCode);const styles = StyleSheet.create({ container: { justifyContent: "center", alignItems: "center", }, textContainer: { flexDirection: "row", alignItems: "center", gap: 8, position: "absolute", }, qrContainer: { position: "absolute", justifyContent: "center", alignItems: "center", }, text: { color: "#000", fontSize: 16, fontWeight: "500", }, buttonsContainer: { position: "absolute", bottom: 20, flexDirection: "row", gap: 12, }, button: { backgroundColor: "#fff", paddingHorizontal: 30, paddingVertical: 12, borderRadius: 38, flexDirection: "row", alignItems: "center", gap: 6, }, closeButton: { backgroundColor: "#fff", borderRadius: 100, flexDirection: "row", width: 40, height: 40, justifyContent: "center", alignItems: "center", gap: 6, }, buttonText: { color: "#000", fontSize: 14, fontWeight: "700", },});Usage
import { StyleSheet } from "react-native";import { GestureHandlerRootView } from "react-native-gesture-handler";import { StatusBar } from "expo-status-bar";import { useFonts } from "expo-font";import QrCode from "@/components/base/qr-code";export default function App() { const [fontLoaded] = useFonts({ SfProRounded: require("@/assets/fonts/sf-pro-rounded.ttf"), HelveticaNowDisplay: require("@/assets/fonts/HelveticaNowDisplayMedium.ttf"), }); return ( <GestureHandlerRootView style={styles.container}> <StatusBar style="light" /> <QrCode springConfig={{ damping: 28, stiffness: 150, mass: 0.2, }} backgroundColorFocused="#f1f0f4" textStyle={{ fontFamily: fontLoaded ? "HelveticaNowDisplay" : undefined, }} /> </GestureHandlerRootView> );}const styles = StyleSheet.create({ container: { flex: 1, backgroundColor: "#0a0a0a", alignItems: "center", top: 150, }, card: { width: 280, height: 56, backgroundColor: "#151515", borderRadius: 28, justifyContent: "center", alignItems: "center", }, divider: { width: 20 }, text: { fontSize: 35, color: "#fff", },});Props
React Native Reanimated
Expo Blur
Expo Vector Icons
React Native QrCode Styled
