본문 바로가기
카테고리 없음

EXPO) ReactNative의 Animation 구현하기

by 후르륵짭짭 2025. 2. 23.
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
반응형

댓글