스크롤 애니메이션 구현 (Intersection Observer)

yesung·2024년 1월 14일
4
post-thumbnail

메인 페이지의 심심함에서 벗어나고자 스크롤 애니메이션을 덧붙였다.

원래는 react-scroll-motion 라이브러리를 사용해 봤지만 css @keyframe 을 사용하는 게 더 익숙해서 별도의 라이브러리는 사용하지 않았고 스크롤 시 화면 감지를 위해 Intersection Observer를 사용했다.

Intersection Observer 설정

설치

yarn add react-intersectiono-bserver

구현 내용

interface ObserverOptions {
  root?: Element | null;
  rootMargin?: string;
  threshold?: number | number[];
}

interface UseObserverParams {
  target: MutableRefObject<HTMLElement | null>;
  option: ObserverOptions;
}

export const useObserver = ({ target, option }: UseObserverParams) => {
  const [isVisible, setIsVisible] = useState(false);

  useEffect(() => {
    const currentTarget = target.current;
    const observer = new IntersectionObserver(entries => {
      entries.forEach(entry => {
        if (entry.isIntersecting) {
          setIsVisible(entry.isIntersecting);
        } else {
          setIsVisible(entry.isIntersecting);
        }
      });
    }, option);

    if (currentTarget) {
      observer.observe(currentTarget);
    }

    return () => {
      if (currentTarget) {
        observer.unobserve(currentTarget);
      }
    };
  }, [option, target]);

  return { isVisible };
};

Intersection Observer 에서 쓰이는 옵션 타입과 매개변수로 스크롤 시 감지할 대상(Ref)과 옵션을 받아와야 하는 타입도 지정했다.

IntersectionObserver 생성자를 만든 후 해당 참조한 영역 entries 유무를 파악해서 isVisble 상태를 변경했고 다른 페이지에서도 쓰일 것을 대비해서 커스텀 훅으로 분리했다.
→ isVisible은 상태에 따라 css를 조건부 클래스로 주기 위함이다.

컴포넌트 호출

const target = useRef(null);
const { isVisible } = useObserver({ target, option: { threshold: 0.3 } });

<section className={`${styles.fadeWrapper} ${isVisible ? styles.fadeIn : ''}`} ref={target}>
  <div className={styles.fadaContainer}>
    <h1>꼼꼼한 매출 분석까지 도와드려요.</h1>
  </div>
</section>

useObserver 라는 커스텀 훅을 컴포넌트에서 호출하고 훅에서 반환한 isVisible 상태 값으로 스타일 클래스를 조건부로 부여했다.

.fade-wrapper {
  display: flex;
  height: 140rem;
  align-items: center;
  justify-content: center;
  flex-direction: column;
  position: sticky;
}

.fade-in {
  animation: fade-in 2s ease-in-out;
}
@keyframes fade-in {
  0% {
    opacity: 0;
  }
  100% {
    opacity: 1;
  }
}

부모 클래스의 position: sticky 라는 속성을 줬는데 position값을 sticky로 설정할 경우 relative처럼 동작하면서 relative 요소가 없는 경우에는 fixed처럼 동작하는 두 가지 효과를 모두 가질 수 있다.

sticky를 사용하면서 주의해야할 점

  • top, left, bottom, right의 값 여부
  • 부모 요소에 해당하는 모든 엘리먼트의 overflow 값 hidden 여부 확인
    이 중에서 방향을 설정하지 않고 overflow를 hidden으로 값을 부여하면 정상적으로 동작하지 않는다.
    난 여기서 좀 삽질을 했었다...
profile
Frontend Developer

0개의 댓글