React/Typescript

React) Intersection Observer를 사용하여 스크롤 내리면 나타나는 애니메이션 Custom Hook 만들기

Go_nii 2023. 6. 26. 10:23

- 리액트에서 스크롤 시 FadeIn 효과로 서서히 대상이 나타나는 애니메이션을 만드는 방법을 알아보자.

- 애니메이션을 만들때 Intersection Observer를 사용하면 과부하를 방지할 수 있다.


Intersection Observer API

Intersection Observer API는 대상 요소와 상위 요소 또는 최상위 문서 뷰포트의 교차점에서의 변화를 비동기적으로 관찰할 수 있는 방법을 제공합니다.

 

지금까지 요소의 가시성 또는 두 요소의 상대적 가시성을 감지하는 것은 어려운 작업이었으며, 솔루션이 안정적이지 않아 브라우저와 사용자가 액세스하는 사이트가 느려지기 쉬웠습니다. 웹이 발전함에 따라 이러한 종류의 정보에 대한 필요성이 커졌습니다. 교차점 정보는 다음과 같은 여러 가지 이유로 필요합니다:

 - 페이지 스크롤 시 이미지 또는 기타 콘텐츠의 지연 로딩.
- 스크롤할수록 점점 더 많은 콘텐츠가 로드되고 렌더링되어 사용자가 페이지를 넘길 필요가 없는 '무한 스크롤' 웹사이트를 구현하는 경우.
- 광고 수익을 계산하기 위해 광고의 가시성을 보고합니다.
- 사용자가 결과를 볼 수 있는지 여부에 따라 작업 또는 애니메이션 프로세스를 수행할지 여부를 결정합니다.

 

** Intersection Observer 생성하기

 let observer: any;
const { current } = dom;

if (current) {
   observer = new IntersectionObserver(handleScroll, { threshold: 0.5 });
   observer.observe(current);

   return () => observer && observer.disconnect();
}

threshold가 1.0이면 루트 옵션으로 지정된 요소 내에 대상의 100%가 표시될 때 콜백이 호출됩니다.

 

** 애니메이션 나타나는 방향 정하기

 

CSS의 translate3d 속성을 이용하면  HTML 요소의 모양, 크기, 위치 등을 입체적으로 변형시킬 수 있습니다.

translate3d() 메소드는 현재 위치에서 해당 요소를 주어진 x축과 y축, z축의 거리만큼 이동시킵니다.

주어진 거리가 양수이면 해당 축의 양의 방향으로, 음수이면 해당 축의 음의 방향으로 이동시킵니다.

사용자가 넘겨준 문자열에 해당하는 기능을 수행하기 위해 switch ~ case 문을 사용하여 이동 방향을 지정합니다.

const handleDirection = (name: string) => {
    switch (name) {
      case "up":
        return "translate3d(0, 50%, 0)";
      case "down":
        return "translate3d(0, -50%, 0)";
      case "left":
        return "translate3d(50%, 0, 0)";
      case "right":
        return "translate3d(-50%, 0, 0)";
      default:
        return;
    }
  };

** 전체 코드

useControlFadeIn.ts

import { useCallback, useEffect, useRef } from "react";

export const useScrollFadeIn = (direction = "", duration = 1, delay = 0) => {
  const dom = useRef();

  const handleDirection = (name: string) => {
    switch (name) {
      case "up":
        return "translate3d(0, 50%, 0)";
      case "down":
        return "translate3d(0, -50%, 0)";
      case "left":
        return "translate3d(50%, 0, 0)";
      case "right":
        return "translate3d(-50%, 0, 0)";
      default:
        return;
    }
  };

  const handleScroll = useCallback(([entry]: any) => {
    const { current }: any = dom;

    if (entry.isIntersecting) {
      current.style.transitionProperty = "opacity transform";
      current.style.transitionDuration = `${duration}s`;
      current.style.transitionDelay = `${delay}s`;
      current.style.opacity = 1;
      current.style.transform = "translate3d(0, 0, 0)";
    }
  }, []);

  useEffect(() => {
    let observer: any;
    const { current } = dom;

    if (current) {
      observer = new IntersectionObserver(handleScroll, { threshold: 0.5 });
      observer.observe(current);

      return () => observer && observer.disconnect();
    }
  }, [handleScroll]);

  return {
    ref: dom,
    style: {
      opacity: 0,
      transform: handleDirection(direction),
    },
  };
};

 

다른 파일에서 불러올 때는

MainBanner.tsx

import { useScrollFadeIn } from "../hooks/useControlFadeIn";

const MainBanner = ({element}: any) => {

    const backAnimatedItem:any = useScrollFadeIn('', 1.5);
    	...
    return (
        <div className="w-full h-[70vh] md:h-[100vh] mx-auto my-0 relative flex flex-col text-center pt-[7.5rem]" {...backAnimatedItem}>
        	...
        </div>
    );
}

관찰할 dom 요소에 애니메이션을 지정해주면 된다.

 

** 활용 예시

 

참고: https://developer.mozilla.org/en-US/docs/Web/API/Intersection_Observer_API

 

Intersection Observer API - Web APIs | MDN

The Intersection Observer API provides a way to asynchronously observe changes in the intersection of a target element with an ancestor element or with a top-level document's viewport.

developer.mozilla.org

참고: https://darrengwon.tistory.com/1179

 

custom hook (4) : intersection observer를 활용한 useScrollFadeIn hook

intersection observer 스크롤을 내리다가 특정 element가 보이는 시점에 애니메이션이 동작하는 UI를 보셨을 겁니다. dom요소의 가시성을 측정하는 intersection observer를 활용하면 됩니다. 찾아보니까 이것

darrengwon.tistory.com