Qr Code

A tap to expand qr view that smoothly morphs

Last updated on

Edit on GitHub

Manual

Install the following dependencies:

npm install react-native-reanimated expo-blur @expo/vector-icons react-native-qrcode-styled

Copy 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