react skeleton component 만들기

G-NOTE·2023년 8월 27일
1

React

목록 보기
25/27

react 이미지 최적화 하기 (react-image-file-resizer) 에서 이어지는 글..

문제 상황

최적화 글에서 적은 것처럼 이미지 리사이징이 되지 않아 뷰포트 로딩 속도가 굉장히 길어졌고 이미지가 페인팅 되는 과정이 그대로 노출되어 사용자 경험 개선을 위해 skeleton 컴포넌트를 넣었다.

해결

code

import React from "react";
import styled from "styled-components";

const Skeleton = () => {
  return (
    <StSkeleton>
      <StShimmerContainer>
        <StShimmer />
      </StShimmerContainer>
    </StSkeleton>
  );
};

export default Skeleton;

const StSkeleton = styled.div`
  width: 100%;
  height: 100%;
  background-color: ${({ theme }) => theme.colors.gray};
  position: relative;
`;

const StShimmerContainer = styled.div`
  width: 100%;
  height: 100%;
  position: absolute;
  top: 0;
  left: 0;
  animation: loading 2.5s infinite;
  @keyframes loading {
    0% {
      transform: translateX(-150%);
    }
    50% {
      transform: translateX(-60%);
    }
    100% {
      transform: translate(150%);
    }
  }
`;

const StShimmer = styled.div`
  width: 70%;
  height: 100%;
  background: linear-gradient(
    90deg,
    rgba(255, 255, 255, 0) 0%,
    rgba(255, 255, 255, 0.4) 25%,
    rgba(255, 255, 255, 0.6) 50%,
    rgba(255, 255, 255, 0.4) 75%,
    rgba(255, 255, 255, 0) 100%
  );
  box-shadow: 0 0 30px 30px rgba(255, 255, 255, 0.05);
`;
import { useState, useEffect } from "react";
import styled from "styled-components";
import Skeleton from "./Skeleton";

interface ImageLoaderProps {
  src: string;
}

const ImageLoader = ({ src }: ImageLoaderProps) => {
  const [loading, setLoading] = useState(true);

  useEffect(() => {
    const image = new Image();
    image.onload = () => setLoading(false);
    image.src = src;
  }, [src]);

  if (loading) {
    return <Skeleton />;
  } else {
    return <StImg src={src} alt="loaded preview" />;
  }
};

export default ImageLoader;

const StImg = styled.img`
  width: 100%;
  height: 100%;
  object-fit: cover;
`;
  • new Image()로 이미지 객체를 생성하고 img url를 넣어 이미지 컴포넌트가 생성될 때까지 <Skeleton /> 컴포넌트로 대체한다.

효과

profile
FE Developer

0개의 댓글