[Typescript]Intersection Observer를 사용하여 스크롤 애니메이션 구현하기

JunSeok·2023년 8월 11일
1
post-thumbnail

상황

Money Mindset이라는 웹 서비스를 구현한 뒤, Main 페이지를 꾸미기 위해 요즘 유행하는 스크롤 애니메이션을 구현하고자 한다.

사용 도구

구글과 유튜브에서 스크롤 애니메이션 구현 방법을 알아보다가 코딩애플님의 영상을 보고 Intersection Observer을 사용하기로 결정했다.

Intersection Observer

Intersection Observer는 루트 요소와 타겟 요소의 교차점을 관찰하는 API로 자바스크립트에 내장되어 있어 신뢰성을 보장받은 공식 API이며, 기존의 scroll 이벤트보다 성능상으로 훌륭하다.

기존의 scroll 이벤트는 scroll이 발생할 때마다 특정 element가 화면에 존재하는지 계속 체크해야 하며, 작업들 모두 동기적으로 실행되기 때문에 성능적으로 과부하가 올 수 있다. 이에 반해 Intersection Observer는 교차점 관찰 작업을 비동기적으로 실행한다.

무엇보다도 Intersection Observer API를 사용하면 매우 편리하게 내가 원하는 스크롤 애니메이션을 구현할 수 있다.

사용방법

  1. IntersectionObserver 인스턴스를 생성하고 파라미터로 callback functionoptions값을 전달한다.
  2. options 값에는 threshold를 사용했는데, 이는 타겟이 화면에 노출되는 정도를 설정하여 언제 callback function을 실행시킬 것인지 결정하는 값이다. 기본값은 0이며, 예를 들어 0.5라고 설정한다면 이는 화면에 타겟 element가 50% 보였을 때 callback function을 실행한다고 설정한 것이다.
  3. IntersectionObserver가 observe할 대상을 추가한다.
  4. 타겟 element가 감지되면 callback function이 실행되고 파라미터로 entries와 observer를 받는다. entries 배열에는 감지된 타겟 element가 추가된다.
  5. callback function에서 전달받은 entries 배열을 forEach로 확인하면서 inIntersecting으로 노출 여부를 확인한다.
  6. 노출이 되었다면 이전에 useState로 설정한 inviewPort을 true로 설정하고 이 값을 이용하여 스크롤 애니메이션을 구현한다.
import { useEffect, useRef, useState } from "react"

const FourthSection = () => {
    const element = useRef<HTMLDivElement | null>(null)
    const [inviewPort, setInviewPort] = useState<boolean>(false)
    useEffect(() => {
        const observerCallback = (entries: IntersectionObserverEntry[]) => {
          // callback function에서 전달받은 entries 배열을 forEach로 확인하면서 inIntersecting으로 노출 여부를 확인한다.
            entries.forEach((entry) => {
                if (entry.isIntersecting) {
                    setInviewPort(true)
                }
                else setInviewPort(false)
            })
        }
        // IntersectionObserver 객체를 생성하고 callback function과 option값을 전달한다. threshold를 0.5로 설정
        const observer = new IntersectionObserver(observerCallback, {
            threshold: 0.5
        })
        // IntersectionObserver가 observe할 대상을 추가한다.
        if (element.current) {
            observer.observe(element.current)
        }
    }, [])
    return (
        <section className={`w-full min-h-[calc(100vh-5rem)] bg-gray-100`}>
            <div className="flex flex-col items-center justify-center w-full h-full">
              // 노출이 되었다면 이전에 useState로 설정한 inviewPort을 true로 설정하고 이 값을 이용하여 스크롤 애니메이션을 구현한다.
                <div ref={element} className={`${inviewPort ? "opacity-100" : "opacity-0"} transition-all duration-1000`}>
                    <div className="text-4xl font-semibold leading-normal whitespace-pre-wrap">
                        자신의 수입과 지출을 확인하고 <br />
                        지출 위치까지 한 눈에 파악!
                    </div>
                </div>
            </div>
        </section>
    )
}

export default FourthSection
profile
최선을 다한다는 것은 할 수 있는 한 가장 핵심을 향한다는 것

2개의 댓글

comment-user-thumbnail
2023년 8월 11일

감사합니다. 이런 정보를 나눠주셔서 좋아요.

1개의 답글