본문 바로가기

리액트

진행률에 따른 텍스트 색상이 바뀌는 프로그레스바 구현하기

진행률에 따른 텍스트 색상이 바뀌는 프로그레스바

다음과 같은 형태로 프로그래스 바를 구현해달라는 디자인 요청이 왔다.

 

 

.....?

 

진행률에 따른 가운데 퍼센테이지의 색상이 글자를 지나감에 따라서 색상을 반전시켜달라는 것이 디자인 요청의 목적이었다.

 

(한번도 해본적 없는데...)

 

일단은 구현을 해보기로 했다.

 

import styled from 'styled-components';
import { alignItemCenter, flex, justiCenter } from '../style/cssCommon';

type ProgressBarProp = {
  progressPer: number;
};

const ProgressWrap = styled.div`
  width: 100%;
  height: 40px;
  position: relative;
  background-color: #f2f4f6;
  border-radius: 4px;
  font-size: 14px;
`;

const Progress = styled.div<{ width?: number }>`
  &.back {
    border-radius: 4px;
    .bg {
      width: ${(props) => props.width}%;
      position: absolute;
      height: 40px;
      background: linear-gradient(to left, #a86cea, var(--purple-50-main));
      border-radius: 4px;
      transition: width 0.5 ease-out;
    }
  }
`;

const Text = styled.div<{ width?: number }>`
  &.back_num {
    ${flex}
    ${alignItemCenter}
    ${justiCenter}
    
    position: relative;
    height: 40px;
    color: #000;
    z-index: 2;
  }

  &.front_num {
    ${flex}
    ${alignItemCenter}
    ${justiCenter}
    
    z-index: 2;
    position: absolute;
    top: 0;
    right: 0;
    bottom: 0;
    left: 0;
    color: #fff;
    height: 40px;
  }
`;

export default function ProgressBar({ progressPer }: ProgressBarProp) {
  return (
    <ProgressWrap>
      <Progress className="back" width={progressPer}>
        <div className="bg"></div>
        <Text className="back_num">{progressPer}%</Text>
      </Progress>
      <Progress>
        <Text className="front_num" width={progressPer}>
          {progressPer}%
        </Text>
      </Progress>
    </ProgressWrap>
  );
}

 

처음 시도에는 검은색 텍스트와 흰색 텍스트를 같은 위치상에 놓고 프로그레스바가 올라갈 때 가리는 형태로 구현해보기로 하였다.

 

첫 번째 결과물

음.. 예상대로 동작하지 않는다..

그렇게 여러 래퍼런스를 찾아보며 css mask 속성을 찾아서 적용해보기로 했다!

 

import styled from 'styled-components';
import { alignItemCenter, flex, justiCenter } from '../style/cssCommon';

type ProgressBarProp = {
  progressPer: number;
};

const ProgressWrap = styled.div`
  width: 100%;
  height: 40px;
  position: relative;
  background-color: #f2f4f6;
  border-radius: 4px;
  font-size: 14px;
`;

const Progress = styled.div<{ width?: number }>`
  &.back {
    border-radius: 4px;
    .bg {
      width: ${(props) => props.width}%;
      position: absolute;
      height: 40px;
      background: linear-gradient(to left, #a86cea, var(--ai-purple-50-main));
      border-radius: 4px;
      transition: width 0.5 ease-out;
    }
  }
`;

const Text = styled.div<{ width?: number }>`
  &.back_num {
    ${flex}
    ${alignItemCenter}
    ${justiCenter}
    
    position: relative;
    height: 40px;
    color: #000;
    z-index: 2;
  }

  &.front_num {
    ${flex}
    ${alignItemCenter}
    ${justiCenter}
    
    z-index: 2;
    position: absolute;
    top: 0;
    right: 0;
    bottom: 0;
    left: 0;
    color: #fff;
    height: 40px;
    mask: url(imgUrl)
      0 0 no-repeat;
    mask-size: ${(props) => props.width}% 30px;
  }
`;

export default function ProgressBar({ progressPer }: ProgressBarProp) {
  return (
    <ProgressWrap>
      <Progress className="back" width={progressPer}>
        <div className="bg"></div>
        <Text className="back_num">{progressPer}%</Text>
      </Progress>
      <Progress>
        <Text className="front_num" width={progressPer}>
          {progressPer}%
        </Text>
      </Progress>
    </ProgressWrap>
  );
}

 

mask 속성을 사용하여 프로그래스 바가 올라가는 만큼 mask의 width도 늘려서 뒷 요소를 가리고 흰 글자를 보이게 하도록 수정해보았다.

 

결과는...

 

성공

성공적!!

 

구현 방법을 몰라서 GPT와 여러 래퍼런스를 이틀넘게 뒤졌는데 ㅠㅠ

겨우 구현에 성공하였다....ㅠㅠ

 

하지만 아직 mask 속성에 대해 정확히 몰라 더 공부가 필요할 것 같다

 

아래는 최종 결과물