본문 바로가기

REACT

React로 Intersection Observer 없이 Viewport 감지 구현하기

728x90

Intersection Observer API는 특정 엘리먼트가 viewport에 보일 때 관찰할 수 있게 해준다. 주로 페이지 성능 개선을 위해 Lazy Image Loading 등에 사용한다. 하지만 여기서는 Intersection Observer를 사용하지 않고, React와 TypeScript로 비슷한 기능을 직접 구현해보자.

1. 화면 크기 구하기 및 이벤트 감지

엘리먼트가 화면에 보이는지 관찰하려면, 먼저 windowwidth, height를 알아야 한다. 화면 크기가 변할 때마다 관찰 함수를 실행하도록 resize 이벤트 핸들러를 추가한다. 또한, 스크롤이 발생할 때도 엘리먼트 위치를 재확인해야 하므로 scroll 이벤트 핸들러도 등록한다.

// useEffect 예시
useEffect(() => {
  handleObserveElement();
}, [handleObserveElement]);

useEffect(() => {
  window.addEventListener('resize', handleObserveElement);
  return () => {
    window.removeEventListener('resize', handleObserveElement);
  };
}, [handleObserveElement]);

useEffect(() => {
  window.addEventListener('scroll', handleObserveElement);
  return () => {
    window.removeEventListener('scroll', handleObserveElement);
  };
}, [handleObserveElement]);

2. handleObserveElement 함수 작성

요소가 화면에 보이는지 판단하는 핵심 함수다.
현재 뷰포트 크기를 구하고, 관찰 대상 엘리먼트의 위치를 getBoundingClientRect()로 구한다.
엘리먼트의 Y좌표가 화면 높이보다 작으면 (즉, 화면 안에 들어오면) isViewing 상태를 true로 변경한다.

const [isViewing, setIsViewing] = useState<boolean>(false);

const handleObserveElement = useCallback(() => {
  // 뷰포트 크기 구하기
  let winH = window.innerHeight;
  let winW = window.innerWidth;

  // 관찰할 엘리먼트 위치 정보
  let info1 = document.getElementById('box' + type)?.getBoundingClientRect();

  // Y축 위치 확인 (임시로 200px 여유를 둠)
  if (Number(info1?.y) < winH - 200 && Number(info1?.x) < winW) {
    setIsViewing(true);
  }
}, [type]);

여기서 200px을 빼준 이유는, 엘리먼트가 화면에 보이기 시작할 때 눈으로 확인하기 쉽게 하기 위해서다.

3. 화면에 보여질 때 스타일 변경하기

isViewing 상태가 true일 때, 엘리먼트 배경색을 변경해서 사용자에게 시각적으로 표시한다.

return (
  <Box id={'box' + type} isView={isViewing}>
    {type}
  </Box>
);

const Box = styled.div<IBox>`
  background: pink;
  width: 200px;
  aspect-ratio: 1 / 1;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 2rem;

  ${({ isView }) =>
    isView &&
    css`
      background-color: green;
      transition: 0.5s all ease-in;
    `}
`;

---

 

| 실행화면

 

 

| 사진 적용 

 

728x90
반응형