Logo
Published on
·7 min read

진행률을 표시할 Progress Bar(Circle) 만들기 - React Native

개요

"소소한 단어장" 앱에는 해당 그룹 학습 진행률과 퀴즈 진행률을 표시하는 Progress Circle이 있습니다.

어떻게 적용했는지 정리해보겠습니다.

적용 화면

아래 이미지는 현재 서비스 되고 있는 앱에 적용된 화면입니다.

Progress Circle 적용 화면

구현 과정

❗ 예제 코드의 스타일 및 함수 등은 포스팅을 위해 삭제 및 생략한 부분들이 있습니다.

1. react-native-svg 설치

svg를 이용하여 Progress Circle을 구현할 것이기 때문에 react-native-svg를 설치합니다.

npm i react-native-svg

2. Progress Circle 컴포넌트 생성

Making a progress circle in React 코드를 기반으로 수정했습니다.

참고한 코드는 React용으로 작성되어 있어서 React Native에서 사용할 수 있도록 그리고 제 사용 용도에 맞도록 수정했습니다.

Pie.tsx

// https://dev.to/jackherizsmith/making-a-progress-circle-in-react-3o65

import Svg, {Circle, G, Text, TSpan} from 'react-native-svg';

const cleanPercentage = (percentage) => {
  const tooLow = !Number.isFinite(+percentage) || percentage < 0;
  const tooHigh = percentage > 100;
  return tooLow ? 0 : tooHigh ? 100 : +percentage;
};

const PieCircle = ({ color, pct, radius = 50, borderWidth = 2 }:{
  color: string;
  pct?: number;
  radius: number;
  borderWidth: number;
}) => {
  const circ = 2 * Math.PI * radius;
  const strokePct = ((100 - pct) * circ) / 100;
  return (
    <Circle
      r={radius}
      cx={radius + borderWidth}
      cy={radius + borderWidth}
      fill="transparent"
      fillRule={"nonzero"}
      stroke={pct > 0 ? color : ""}
      strokeWidth={borderWidth}
      strokeDasharray={circ}
      strokeDashoffset={pct ? strokePct : 0}
      strokeLinecap="round"
    ></Circle>
  );
};

const PieText = ({ percentage, title }) => {
  return (
      <Text
        x="50%"
        y="50%"
        textAnchor="middle"
      >
        <TSpan x="50%" dy={0} fontSize={11} fontWeight={'bold'} fill={'#151A20'}>{title}</TSpan>
        <TSpan x="50%" dy={14} fontSize={10} fill={'#151A20'}>{`${percentage}%`}</TSpan>
      </Text>
  );
};

const Pie = ({ percentage, color, title, radius = 50, borderWidth = 2}:{
  percentage: number;
  color: string;
  title: string;
  radius: number;
  borderWidth: number;
}) => {
  const pct = cleanPercentage(percentage);
  const fullSize = (radius * 2)+(borderWidth*2);
  return (
    <Svg width={fullSize} height={fullSize}>
      <G transform={`rotate(-90 ${radius + borderWidth} ${radius + borderWidth})`} fill={'white'}>
        <Circle
          r={radius}
          cx={radius + borderWidth}
          cy={radius + borderWidth}
          fill={'#FFFFFF'}
        ></Circle>
        <PieCircle borderWidth={borderWidth} radius={radius} color="#e3e3e3" pct={100} />
        <PieCircle borderWidth={borderWidth} radius={radius} color={color} pct={pct} />
      </G>
      <PieText percentage={pct} title={title} />
    </Svg>
  );
};

export default Pie;

3. Progress Circle 적용

  • <Pie ... /> 태그 사용
    • radius: 반지름 크기 지정
    • borderWidth: Progress Circle의 두께 지정
    • percentage: Progress Circle의 퍼센트 지정
    • color: Progress Circle 색상 지정
    • title: Progress Circle 텍스트 지정
    <View>
      <TouchableOpacity
        accessibilityLabel={'학습'}
        onPress={() => {}}>
        <Pie radius={28} borderWidth={4} percentage={getLearnRate()} color={'#4285F4'} title={'학습'} />
      </TouchableOpacity>
      <View style={{width: 8}} />
      <TouchableOpacity
        accessibilityLabel={'퀴즈'}
        onPress={() => {}}>
        <Pie radius={28} borderWidth={4} percentage={getQuizRate()} color={'#4285F4'} title={'퀴즈'} />
      </TouchableOpacity>
    </View>