Job/React

[React] 도넛 프로그레스 animation 적용 (style-component, transition)

웃던날 2023. 6. 23. 17:10

1. 구현목적

구현을 하게된 계기는 도넛 progress를 만들어야하는데, 라이브러리는 사용하고싶지 않았고, 내가 직접 한번 만들어보고 싶었다. 그것도 컴포넌트로 말이다!

사용한 라이브러리는 style-component밖에 없다.

혹시나 스타일 컴포넌트에서 props를 사용못하는 사람들은 document 한번 읽고오길 권장한다...

2. 구현 코드

import { styled } from "styled-components";
import { CustomFlex } from "../theme/style";

interface DonutBackType {
    width : number,
    height : number,
    frontColor : string,
    backColor : string,
    donutper : number,
} 

interface DonutFrontType {
    width : number,
    height : number,
}

//도넛 뒷배경
export const DonutBack = styled.div<DonutBackType>` 
	display:'flex';
    justify-content:'center';
    align-items:'center';
    width: ${({width}) => width+'rem'};
    height: ${({height}) => height+'rem'};
    margin: 0 auto;
    border-radius: 50%;
    position: relative;
    text-align: center;

    @property --tranDonutPer{
        syntax: '<percentage>'; /* its type */
        inherits: false;
        initial-value: ${({donutper}) => `${donutper}%`}; /* the initial value */
    }
    transition:--tranDonutPer 0.5s;
    background : ${({frontColor,backColor,donutper}) => `conic-gradient(${frontColor} 0% var(--tranDonutPer), ${backColor} var(--tranDonutPer) 100%)`};
`

//도넛 앞배경 (텍스트 들어가면서, 도넛모양을 잡아줌)
export const DonutFront = styled(CustomFlex)<DonutFrontType>`
	display:'flex';
    justify-content:'center';
    align-items:'center';
    position:absolute;
    width:${({width}) => width+'rem'};
    height:${({height}) => height+'rem'};
    border-radius: 50%;
    top:50%;
    left:50%;
    transform: translate(-50%,-50%);

    background-color:white;
`

도넛이 채워지는 색상은 frontColor, 채워지지 않은 색상은 backColor로 설정해주었다.

배경은 따로 라이브러리를 사용하지않고, conic-gradient를 사용해주었다. 도넛 프로그레스에는 최적의 스타일이라고 생각했다.

자세하게 봐야할 내용은 DonutBack 컴포넌트인데, 여기서 transition을 @property 변수로 적용했다.

conic-gradient는 transition에 변화가 감지가 되지 않는다는 얘기를 들었고, 역시 했을때 제대로 감지되지 않았다.

그런데 @property 변수는 transition으로 감지가 가능하다고 한다.

여기서 @property에 들어가는 속성들은 다음과 같다.

  1. syntax : 변수의 유형을 정의한다. 여기서는 percentage를 선언하였으며, 퍼센트 단위를 사용한다고 선언한것이다.
  2. inherits : css Custom Property가 상속되는지 여부를 나타낸다. 해당 코드에서는 false로 설정되어있으므로, tranDonutPer은 상속되지 않는다.
  3. initial-value : css customProperty의 초기값을 나타낸다. 해당코드에서는 props로 가져온 percent값을 기본값으로 설정하였다.

자 그러면 컴포넌트에서는 어떻게 사용하는지 살펴보자.

import React from 'react';
import { DonutBack, DonutFront } from './ComponentStyle';
import { CustomSpanText } from '../theme/style';


export const DonutGraph = () => {

    const [donutPer , setDonutPer] = React.useState(0);
    
    return(
        <>
            <DonutBack 
                width={9} 
                height={9} 
                frontColor={'#FFC888'}
                backColor={'#DBDFE8'} 
                donutper={donutPer} 
            >
                <DonutFront 
                    width={7}
                    height={7}
                >
                    <CustomSpanText>{donutPer}%</CustomSpanText>
                </DonutFront>
            </DonutBack>
            <button onClick={()=>{
                if(donutPer < 100){
                    setDonutPer(donutPer+10);
                }
            }}>
                <p>올리기</p>
            </button>
        </>
    )
}

출력 코드는 별로 어려울게 없다.

그냥 컴포넌트만 불러와서 색상 및 donutPer값만 넣어주면된다.