Header를 스크롤에 따라 반응시키기

broccoli·2021년 6월 25일
3

toy

목록 보기
2/2
post-thumbnail

헤더를 스크롤업할때 스크롤다운할때 그리고 스크롤을 멈췄을 때에 따라 반응시키기 구현을 해보자.

1. 스크롤 이벤트 등록

우선 스크롤 이벤트를 등록하고 핸들러를 바인딩해준다.
nextjs에서 다음과 같이 windowscroll이벤트 등록시 이벤트 핸들러가 트리거 되지 않는다.

  • next@10.1.3

1-1. 스크롤이벤트 트리거 안됨 예시

  const handleScroll = useCallback((e) => {
    console.log(e)
  }, [])
  
  useEffect(() => {
    window.addEventListener('scroll', handleScroll)
    return () => {
      window.removeEventListener('scroll', handleScroll)
    }
  }, [])

이벤트 핸들러 트리거 안됨.
관련이슈 참고링크 : Window scroll event not fired

1-2. 스크롤이벤트 트리거 잘됨 예시

useRef로 스크롤를 사용하는 컴포넌트를 참조걸고 이벤트 등록하면 트리거 잘됨.

  const handleScroll = useCallback((e) => {
    console.log(e)
  }, [])
  
  useLayoutEffect(() => {
    if (layoutRef.current) {
      layoutRef.current.addEventListener('scroll', handleScroll)
      return () => layoutRef.current.removeEventListener('scroll', handleScroll)
    }
  }, [])

설명은 next.js에서 스크롤등록하기를 참조한다.

2. 쓰로틀과 디바운스 활용하기

스크롤이벤트같이 한번에 수십번이 실행될 수 있는 이벤트는 제한을 걸어줘야 성능에 부담이 안가고 불필요한 동작을 막을 수 있다.

쓰로틀과 디바운스는 개념이 다른데 자세한 설명과 구현예시는 쓰로틀과 디바운스관련 설명링크를 참조해라.

2-1 쓰로틀과 디바운스를 활용한 헤더제어하기

헤더를 보였다 안보였다 제어하기 위해서는 스크롤이 다음 2가지 상태가 필요하다.

  • 스크롤위로이동
  • 스크롤아래로이동

여기에 추가로 스크롤멈춤까지 감지할 수 있는데, 위에서 언급한 쓰로틀디바운스를 활용해서 감지가 가능하다.

아래는 구현 예시이다.

// 스크롤이 내려가고 올라오는지 확인해주는 핸들러
  const handleScroll = useCallback(
    (e) => {
      // 현재위치와 이전 위치의 차를 계산한다.
      const diff = e.target.scrollTop - prevY
      if (diff > 0) {
        console.log('내려가는중')
        // 헤더를 보여줄지 말지 set
        setIsHeaderShow(false)
      } else if (diff < 0) {
        console.log('올라가는중')
        setIsHeaderShow(true)
      }
      setPrevY(e.target.scrollTop)
    },
    [prevY]
  )
// 스크롤이 멈췄는지 확인해주는 핸들러
  const stopScroll = useCallback((e) => {
    console.log('🥎🥎🥎멈춤', e.target.scrollTop)
    // 스크롤이 가장 끝까지 올라가면 헤더보임.
    // 스크롤이 멈추면 헤더안보임
    if (e.target.scrollTop === 0) {
      setIsHeaderShow(true)
    } else {
      setIsHeaderShow(false)
    }
  }, [])
  // 스크롤의 위 아래 이동감지는 쓰로틀이 이용
  const throttleScroll = useThrottle(handleScroll, 300)
  // 스크롤의 멈춤은 디바운스를 이용한다.(디바운스가 마지막 그룹의 이벤트를 노티해준다는 특성이용)
  const debounceScroll = useDebounce(stopScroll, 1500)
  const scrollDetectHandler = useCallback(
    (...e) => {
      throttleScroll(...e)
      debounceScroll(...e)
    },
    [prevY]
  )
  // DOM 제어시 useLayoutEffect를 사용하는게 성능상 좋음.
  useLayoutEffect(() => {
    if (scrollRef.current) {
      scrollRef.current.addEventListener('scroll', scrollDetectHandler)
    }
    return () => {
      if (!scrollRef.current) return
      scrollRef.current.removeEventListener('scroll', scrollDetectHandler)
    }
  }, [prevY])
profile
🌃브로콜리한 개발자🌟

0개의 댓글