간단한 로딩 스피너 만들기

여름노래불러줘·2023년 12월 7일
0

프론트엔드

목록 보기
1/3

라이브러리 없이 React 컴포넌트를 만들어보고 있는 중인데,
한 번 만들고 땡이 아니라 기록으로 남기면 나중에 재사용할 일이 있을지도 모르고, 타인에게 도움이 될 거 같기도 해서 기록을 남긴다.

프론트엔드 라이브러리를 써도 좋지만, 직접 만들어 쓰는 낭만이란...

개발환경은 create react app 명령어
npx create-react-app@latest --template=typescript .
으로 설정하였고, 가급적 CSS 만 이용하였다.

로딩 스피너를 만들기 위해 svg 태그를 이용할 것이며
비트맵 이미지와 벡터 그래픽에 대한 이야기는 생략한다.

SVG 컴포넌트

children: circle 태그가 들어간다.
svgProps: children 은 위에서 한 번 선언했으니 이를 제외한 svg 태그에 넣을 수 있는 파라미터들.

// src/components/Svg/Svg.tsx

import { ReactNode, SVGProps } from "react";

interface Props {
  children?: ReactNode | ReactNode[];
  svgProps?: Omit<SVGProps<SVGSVGElement>, 'children'>;
}

function Svg({ children, svgProps }: Props) {
  return (
    <svg {...svgProps} >
      {children}        
    </svg>
  );
}

export default Svg;

Loading 컴포넌트

위에서 선언한 Svg 컴포넌트를 불러온 다음 viewBox 및 width 와 height 속성을 size Prop 을 통해 받아오도록 설정하고 circle 에 필요한 속성들도 전달한다.
이 컴포넌트는 기본 가로 세로 64, 테두리 두께 8 을 가진다.

circle tag props

  • cx: 원의 중심 x 좌표이다. 이 컴포넌트는 64x64 사이즈니, 64 / 2 = 32 가 원의 중심 x 좌표가 된다.
  • cy: 원의 중심 y 좌표이다. 마찬가지이다.
  • r: 반지름은 우선 size 에서 테두리 크기를 뺀 후 2로 나눈 값이다.
    테두리 크기를 빼주지 않으면 컴포넌트가 원래 의도한 크기보다 커진다.
  • fill: 안쪽 채우기 색상은 따로 채우지 않는다.
  • stroke: 테두리 색상은 적당히 맘에 드는 걸로 고른다.
  • strokeWidth: 테두리 크기 역시 기호에 맞게 설정한다.
// src/components/Loading/Loading.tsx

import Svg from "../Svg/svg";
import "./loading.css";

interface Props {
  size?: number;
  sw?: number;
}

function Loading({ size = 64, sw = 8 }: Props) {

  return (
    <Svg svgProps={{
      width: size,
      height: size,
      x: size,
      y: size,
      viewBox: `0 0 ${size} ${size}`,
    }}>
        <circle
          className="loading_circle"
          cx={(size / 2)}
          cy={(size / 2)}
          r={(size - sw) / 2}
          fill="none"
          stroke="red"
          strokeWidth={sw}
        />
    </Svg>
  );
}

export default Loading;

CSS

애니메이션을 제어하기 위해서는 원의 둘레를 알 필요가 있다.

원의 둘레는 구하는 공식은 2𝞹r 이지만, r 에서 sw (strokeWidth) 를 뺀만큼 조정해야 하므로 아래와 같은 공식으로 원의 둘레를 구할 수 있다.

2 × 3.141592 * (64 / 2 - 8) = 약 176

stroke-dasharray 속성은 테두리 선을 점선으로 바꿔준다.
아래 예제에선 점선의 크기를 76, 간격을 100 으로 설정하고 있다.

원의 둘레가 176 이므로 점선의 크기와 간격의 합을 176 으로 설정하면
하나의 점선만 그릴 수 있다.

stroke-dashoffset 속성은 테두리 선이 시작되는 위치를 변경한다.
테두리 선이 시작되는 위치는 3시 방향 (0)을 시작으로 반시계 방향으로 돈다.
12시 방향 시작은 44,
9시 방향 시작은 88,
6시 방향 시작은 132,
9시 방향 시작은 176

을 입력하면 된다.

/* src/components/Loading/Loading.css */

.loading_circle {
    animation: 1s linear 1ms infinite circle_loading;
    stroke-dasharray: 100,76;
    stroke-dashoffset: 44;
    stroke-linecap: round;
}

@keyframes circle_loading {
    0% {
        stroke-dashoffset: 44;
    }

    100% {
        stroke-dashoffset: 220;
    }
}

마지막으로 App 에서 Loading 컴포넌트를 불러오고 size 와 sw props 를 컴포넌트에 전달하면 완성된 로딩스피너를 볼 수 있다.

// src/App.tsx

import './App.css';

import Loading from './components/Loading/Loading';

function App() {
  return (
    <div className="App">
      <div>
        <Loading/>
        <Loading size={24} sw={2}/>
        <Loading size={32} sw={4}/>
        <Loading size={44} sw={6}/>
      </div>
      
    </div>
  );
}

export default App;

그럭저럭 쓸만한 스피너가 완성되었다.

0개의 댓글