react + ts 특정 스크롤 시점에서 CountUp Event(feat. useRef)

miin·2024년 4월 11일
0

Skill Collection [Function]

목록 보기
45/46
  • countUp.tsx
  const [isFocused, setIsFocused] = useState(0)
  const refCurrent = useObserver(1, setIsFocused) //scroll observer
  const [countNumber, setCountNumber] = useState({
    paymentNum: 0,
    countriesNum: 0,
    accrueNum: 0,
  })

  const { paymentNum, countriesNum, accrueNum } = countNumber

  useEffect(() => {
    //scroll이 특정 위치까지 오면 
    if (isFocused > 0) {
      const promises = [
        countUp(872, 'paymentNum'),
        countUp(140, 'countriesNum'),
        countUp(877, 'accrueNum'),
      ]

      Promise.all(promises).then()
    }
  }, [isFocused])

//세개가 모두 동시에 시작하게 하기위해 promise 사용
  const countUp = async (end: number, name: string) => {
    return new Promise<void>((resolve) => {
      //카운터가 각 단계마다 증가하는데 걸리는 시간 각격을 계산하기 위해 사용됨
      // 총 소용시간을 해당 횟수로 나눈 값
      const stepTime = Math.abs(Math.floor(2000 / end))
      let currentNum = 0

      const counter = setInterval(() => {
        currentNum += 1
        setCountNumber((prev) => ({
          ...prev,
          [name]: currentNum,
        }))

        if (currentNum === end) {
          clearInterval(counter)
          resolve()
        }
      }, stepTime)
    })
  }
  
  return(
    //..코드 생략
    {paymentNum}
     <span>+</span>
    )
  • observer.ts
import { useEffect, useRef } from 'react'

export const useObserver = (
  navNumber: number,
  setNavNumber: React.Dispatch<React.SetStateAction<number>>,
) => {
  const options = {}
  const refElement = useRef<HTMLDivElement>(null)

  useEffect(() => {
    //관찰할 대상이 등록되거나 가시서에 변화가 생기면 관찰자는 콜백을 실행한다
    const osv = new IntersectionObserver((entries) => {
      /* isIntersecting: 관찰 대상의 교차 상태(Boolean), 내장 메소드.
      관찰 대상이 들어왔으면 true */
      if (entries[0].isIntersecting) {
        setNavNumber(navNumber)
      } else {
        setNavNumber(0)
      }
    }, options)

    if (refElement.current) {
      //관찰할 대상(요소) 등록
      osv.observe(refElement.current)
    }

    return () => osv.disconnect()
  }, [])

  return refElement
}

0개의 댓글