🧩 아래 처럼 생긴 progress bar 를 만들어보자.
progress bar 가 필요했는데, 여러 라이브러리를 살펴봤지만 맘에 드는게 없기도 했고, 이정도는 내가 개발할 수 있는 정도라 그냥 직접 만들었다. 실제로도 별거 없다.
📍준비물
- react native
- react native animation - https://reactnative.dev/docs/animatedvalue#interpolate
🔨구현
해당 컴포넌트를 다양한 곳에서 사용할 수 있도록, step(count) 값은 외부에서 받도록 변경했다.
컨트롤할 ref 정의 하기
ref 와 같이 쓰는 이유는 값이 변경 되었을 때 렌더링 되지 않고 값을 유지시켜, 애니메이션이 되고 원래 위치로 돌아가는 것을 방지하는 데 사용한다. 즉, 리렌더링 시 값이 초기화 되는 것을 막기 위해서 사용한다.
const loaderValue = useRef(new Animated.Value(0)).current;
load 함수 만들기
가장 기본적인 Animated.timing 를 사용하여 애니메이션을 구현할 수 있다.
css Animation 과 비슷한 기능을 제공하는 api 이다.
toValue 값에는 우리가 받은 count 값이 1, 2, 3 이런식으로 들어올 예정이므로 퍼센테이지로 변경해주기 위해
(count / totalStep) * 100로 값을 정의했다.
그럼 count 가 1 , totalStep 이 10일 경우 toValue 는 10이 된다.
useNativeDriver 는 브릿지를 거쳐 실행되는 애니메이션에 관련된 JS코드를 네이티브에 넘길지 말지 인데,
layout 프로퍼티(width, top, flex 등)에는 적용할 수 없음으로 false 로 적용한다.
(non-layout 프로퍼티에만 적용가능 (transform, opacity 등))
const load = (count: number) => {
Animated.timing(loaderValue, {
toValue: (count / totalStep) * 100,
duration: 500, // 애니메이션이 진행되는 시간
useNativeDriver: false,
}).start();
};
step 이 변경 될때마다 load 함수 실행
useEffect(() => {
load(nowStep)
}, [nowStep]);
interpolate 사용하기
Animated.Value의 변화에 따라 종속적으로 값이 변하도록 하는 기능 이다.
그냥 loaderValue ref의 value 를 가져와서 % 로 변환 후 직접 달아 줄 수도 있으나,
interpolate를 사용하면 Animated.Value 값들을 훨씬 깔끔하게 관리하고 변경할 수 있다..
🫧 extrapolate의 값은 아래와 같다.
- extend (디폴트값)
- 말그대로 input이 늘어나는 대로 늘어난다. 이 값이 디폴트 값이다.
- clamp
- input값이 범위밖을 넘어도 늘어나지 않고 최대치에서 머물러있다.
- identity
- 범위밖을 벗어나면 input 값과 동일해진다.
const width = loaderValue.interpolate({
inputRange: [0, 100], // 0부터 100까지의 값이 들어오면
outputRange: ["0%", "100%"], // 0% ~ 100%로 변경한다.
extrapolate: "clamp" //extrapolate은 clamp 으로 설정한다.
});
이후 컴포넌트에는 아래처럼 적용할 수 있다.
<Animated.View
style={{
backgroundColor: "#AAC9CE",
width, // 이렇게!
height: 3,
borderTopRightRadius:2,
borderBottomRightRadius:2
}} />
🎨 스타일 적용
스타일 까지 적용한 전체 코드는 아래와 같다.
import React, { useEffect, useRef, useState } from 'react'
import { View, StyleSheet, Animated, Text } from 'react-native';
interface IStep {
totalStep: number;
nowStep: number;
}
function ProgressBar({ totalStep, nowStep }: IStep) {
const loaderValue = useRef(new Animated.Value(0)).current;
const load = (count: number) => {
Animated.timing(loaderValue, {
toValue: (count / totalStep) * 100,
duration: 500,
useNativeDriver: false,
}).start();
};
const width = loaderValue.interpolate({
inputRange: [0, 100],
outputRange: ["0%", "100%"],
extrapolate: "clamp"
});
useEffect(() => {
load(nowStep)
}, [nowStep]);
return (
<View>
<View style={styles.bar}>
<Animated.View
style={{
backgroundColor: "#AAC9CE",
width,
height: 3,
borderTopRightRadius:2,
borderBottomRightRadius:2
}} />
</View>
<Text style={styles.step}>{nowStep}/{totalStep}</Text>
</View>
)
}
const styles = StyleSheet.create({
bar: {
width: '100%',
height: 1,
backgroundColor: '#F0F0F0'
},
step: {
color: '#AAC9CE',
fontWeight: '400',
fontSize: 22,
padding: 22,
lineHeight: 22 * 1.3,
textAlign: 'center'
}
});
export default ProgressBar
🐥 사용하기
사용은 아래처럼 할 수 있다.
import React, { useState } from 'react'
import { Button, View } from 'react-native';
import ProgressBar from '../common/ProgressBar';
import BasicLayout, { PageType } from '../layout/BasicLayout';
function Test() {
const [count, setCount] = useState(1);
return (
<View>
<ProgressBar totalStep={12} nowStep={count} />
<Button onPress={() => setCount((prev) => ++prev)} title="1증가"/>
<Button onPress={() => setCount((prev) => --prev)} title="1감소"/>
</View>
)
}
export default Test
'React' 카테고리의 다른 글
React native - custom font 적용과 발생했던 문제를 기록 - IOS (2) | 2022.04.24 |
---|---|
React native - 앱 스킴 만들기 (0) | 2022.04.18 |
React native 로 스와이프 애니메이션 (0) | 2022.04.11 |
🐛 에러로그 - error Failed to build iOS project. We ran "xcodebuild" command but it exited with error code 65. [RN] (4) | 2021.11.25 |
🐛 에러로그 - Multiple commands produce [파일경로] (RN) (0) | 2021.11.25 |