반응형
- 프로젝트: loplat ui
- 키워드: css animation steps, sprite image, 용량 최적화
- 상황
- 로딩 상태일 때 화면에 표시할 Loading UI Component가 필요하다.
- loplat ui library에서 재사용 가능한 컴포넌트를 직접 구현하고자한다.
- 로딩 애니메이션을 위해 gif를 사용할 경우 용량과 성능 문제가 발생한다.
- sprite image와 css animation의 steps 속성을 이용하여 애니메이션 효과를 구현할 수 있다.
- 해결과정
- 먼저 디자이너로부터 30장 이상으로 이루어진 sprite image를 받고 img 태그의 src에 넣었다.
- React Component의 prop으로 duration(애니메이션 재생 시간), scale(컴포넌트 크기 조절), zIndex를 받도록 하고, emotion을 이용하여 img 태그에 css를 입힌다.
export interface SpinnerProps { duration?: number; scale?: number; zIndex?: number; } export const CircleSpinner = ({ duration = 1200, scale = 1, zIndex = 0 }: SpinnerProps): React.ReactElement => { const steps = 60; return ( <SpriteImage src={Circle} alt="" duration={duration} steps={steps} /> ); };
- animation css 와 animation-timing-function 속성인 steps(https://developer.mozilla.org/en-US/docs/Web/CSS/animation-timing-function)로 애니메이션을 구현한다.
steps는 일반적인 애니메이션처럼 연속적으로 움직이지 않고, 계단처럼 뚝뚝 끊겨 움직인다. 즉, 영화 필름처럼 여러장의 사진을 빠르게 보여주어 움직임을 구현하는 원리이다.
이제 이미지가 왼쪽으로 이동(translateX)하면서 애니메이션처럼 움직이는 것을 확인할 수 있다.const SpriteImage = styled.img<ImageProps>` animation: ${(props) => `play ${props.duration}ms steps(${props.steps}) infinite`}; @keyframes play { from { transform: translateX(0); } to { transform: translateX(-100%); } } `;
- 하지만 눈에 보여야하는 것은 sprite image 전체가 아닌 '원 하나'이기 때문에 img를 Wrapper로 감싸주어야한다.
전체 width(6960)와 height(132)는 실제 sprite image 의 크기와 똑같이 설정했다.export const CircleSpinner = ({ duration = 1200, scale = 1, zIndex = 0 }: SpinnerProps): React.ReactElement => { const steps = 60; return ( <Wrapper width={6960} height={132} steps={steps} scale={scale} zIndex={zIndex}> <SpriteImage src={Circle} alt="" duration={duration} steps={steps} /> </Wrapper> ); }; const Wrapper = styled.div<WrapperProps>` width: ${(props) => `calc(${props.width}px / ${props.steps})`}; height: ${({ height }) => height}px; overflow: hidden; transform: scale(${({ scale }) => scale}); z-index: ${({ zIndex }) => zIndex}; `;
Wrapper의 width는 '전체width / steps' 이다.(전체 이미지 중 하나의 원에 해당하는 width)
또한, overflow: hidden으로 하나의 원만 보이도록 했다. - 이제 하나의 원이 계속 돌아가는 애니메이션이 구현되었고, 같은 원리로 cube spinner도 만들었다.
(https://loplat-ui.web.app/?path=/story/components-spinner--default)
- 안드로이드 기기에서 sprite image가 잘리는 버그가 있어(기기 성능 문제로 판단), 사진의 이미지 개수를 절반으로 줄여 이미지 용량을 줄였다.(circle 60 steps -> 30 steps, cube 55 steps -> 28 steps)
webp를 사용하여 용량을 더 줄이려했지만 safari 크로스브라이징 이슈가 있어 png를 사용하기로 했다.(picture tag로 개선 가능할 것 같다.)
- 먼저 디자이너로부터 30장 이상으로 이루어진 sprite image를 받고 img 태그의 src에 넣었다.
반응형
'프론트엔드' 카테고리의 다른 글
번들링 최적화를 통해 import cost 줄이기(1) (0) | 2021.11.25 |
---|---|
Chrome과 안드로이드 기기를 유선/무선 연결하기 (0) | 2021.11.23 |
safari inset, intersection observer 크로스 브라우징하기 (0) | 2021.11.18 |
env 파일의 보안 취약점과 api key 유출에 대비하기 (0) | 2021.11.16 |
Firebase storage 파일 재귀적으로 삭제하기 (0) | 2021.11.13 |