카테고리 없음

EXPO) ReactNative의 Animation 구현하기

후르륵짭짭 2025. 2. 23. 11:39
728x90
반응형

 

해외 어디 바다가에서

안녕하세요. 짭짭이 입니다.

간단하게 작업한 것을 작성하도록 하겠습니다.

 

색에 대해 도와 주는 Helper API

interface Colors {
    white: string;
    black: string;
    grayBG: string;
    neutral: (opacity: number) => string;
}

interface FontWeights {
    // Define font weight properties here if needed
    medium: "500";
    semibold: "600";
    bold: "700";
}

interface Radius {
    // Define radius properties here if needed
    xs: number;
    sm: number;
    md: number;
    lg: number;
    xl: number;
}

interface Theme {
    colors: Colors;
    fontWeights: FontWeights;
    radius: Radius;
}

export const theme: Theme = {
    colors: {
        white: "#fff",
        black: "#000",
        grayBG: "#e5e5e5",
        neutral: (opacity) => `rgba(10, 10, 10, ${opacity})`,
    },
    fontWeights: {
        medium: "500",
        semibold: "600",
        bold: "700",
    },
    radius: {
        xs: 10,
        sm: 12,
        md: 14,
        lg: 16,
        xl: 18,
    }
}

 

Dimension API를 사용하여 화면 크기를 가져와서 percentage로 계산해주는 API

import {Dimensions} from 'react-native';

//window로 부터 width와 height를 가져온다. 그리고 이것을 devicewidth와 deviceHeight로 저장한다.
const {width: devicewidth, height: deviceHeight} = Dimensions.get('window');

export const wp = (percentage: number) => {
    const value = (percentage * devicewidth) / 100;
    return value;
}

export const hp = (percentage: number) => {
    const value = (percentage * deviceHeight) / 100;
    return value;
}

 

RootLayout에 Auth 파일들에 대해서 Header를 가리도록 설정

<Stack screenOptions={{
        headerStyle: {
          backgroundColor: '#f4511e',
        },
        headerTintColor: '#fff',
        headerTitleStyle: {
          fontWeight: 'bold',
        },
      }}>
        <Stack.Screen name='(auth)' options={ () => {
          return {
            headerShown: false
          }
        }}/>
        <Stack.Screen name='(main)' />
        <Stack.Screen name='(room)' />
      </Stack>
      <StatusBar style="auto" />

 

LinearGradient를 아래 명령어로 설치

npx expo install expo-linear-gradient
<LinearGradient
          colors={['rgba(255,255,255,0)', 'rgba(255,255,255,0.5)', 'white', 'white']}
          style={styles.gradiant}
          start={{x:0.5, y:0}}
          end={{x:0.5, y: 0.8}}  />

해당 컴포넌트에 color는 Gradient가 시작 될 때 색의 순서이다. 점점 하양색으로 이동하는 것이다.

그리고 start는 시작점이다. X가 0이라면 가장 좌측, Y가 0이라면 가장 상단. 1은 반대로 생각하면 된다.

 

Animated 컴포넌트는 아래와 같이 사용했다.

import Animated, { FadeInDown } from 'react-native-reanimated';

https://docs.swmansion.com/react-native-reanimated/

 

React Native Reanimated

A powerful animation library that makes it easy to create smooth animations and interactions that run in the UI thread.

docs.swmansion.com

<Animated.View entering={FadeInDown.duration(1000)} style={{flex: 1}}>
       .....
          <View style={styles.contentContainer}>
            <Animated.Text entering={FadeInDown.delay(400).springify()} style={styles.title}>Pixels</Animated.Text>
            <Animated.Text entering={FadeInDown.delay(500).springify()} style={styles.punchline}>Every Pixel Tells a Story</Animated.Text>
            <Animated.View entering={FadeInDown.delay(600).springify()} style={styles.startButton}>
              <Pressable>
                <Text style={styles.startText}>Start Explore</Text>
              </Pressable>
            </Animated.View>
          </View>
      </Animated.View>

이렇게 Animated에 View, Text에 entering으로 원하는 Animation을 쉽게 줄 수 있다.

 

import React, { useState } from 'react';
import { View, Text, TextInput, Button, StyleSheet, Image, Pressable } from 'react-native';
import { Link, router } from 'expo-router';
import { wp, hp } from '@/helper/common';
import { LinearGradient } from 'expo-linear-gradient';
import Animated, { FadeInDown } from 'react-native-reanimated';
import { theme } from '@/constants/theme';

export default function AuthRootPage() {

  return (
    <View style={styles.container}>
      <Image style={styles.bgImage} source={require('../../assets/images/welcome.png')} resizeMode='cover' />
      <Animated.View entering={FadeInDown.duration(1000)} style={{flex: 1}}>
        <LinearGradient
          colors={['rgba(255,255,255,0)', 'rgba(255,255,255,0.5)', 'white', 'white']}
          style={styles.gradiant}
          start={{x:0.5, y:0}}
          end={{x:0.5, y: 0.8}}  />
          <View style={styles.contentContainer}>
            <Animated.Text entering={FadeInDown.delay(400).springify()} style={styles.title}>Pixels</Animated.Text>
            <Animated.Text entering={FadeInDown.delay(500).springify()} style={styles.punchline}>Every Pixel Tells a Story</Animated.Text>
            <Animated.View entering={FadeInDown.delay(600).springify()} style={styles.startButton}>
              <Pressable>
                <Text style={styles.startText}>Start Explore</Text>
              </Pressable>
            </Animated.View>
          </View>
      </Animated.View>
    </View>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
  },
  bgImage: {
    width: wp(100),
    height: hp(100),
    position: 'absolute',
  },
  gradiant: {
    width: wp(100),
    height: hp(65),
    bottom: 0,
    position: 'absolute',
  },
  contentContainer: {
    flex: 1,
    justifyContent: 'flex-end',
    alignItems: 'center',
    gap: 14
  },
  title: {
    fontSize: hp(7),
    color: theme.colors.neutral(0.9),
    fontWeight: theme.fontWeights.bold
  },
  punchline: {
    fontSize: hp(2),
    letterSpacing: 1,
    marginBottom: 10,
    fontWeight: theme.fontWeights.medium
  }, 
  startButton: {
   marginBottom: 50,
   backgroundColor: theme.colors.neutral(0.9),
   padding: 15,
   paddingHorizontal: 90,
   borderRadius: theme.radius.xl,
   borderCurve: 'continuous'
  },
  startText: {
    color: theme.colors.white,
    fontWeight: theme.fontWeights.medium,
    letterSpacing: 1,
    fontSize: hp(3)
  }

});
728x90
반응형