Skip to content
Docs
Motion

스크롤 훅

stable

스크롤 이벤트에 반응하는 뷰포트 감지, 진행률, 방향 감지 훅

Import

import {
  useScrollReveal,
  useScrollProgress,
  useScrollDirection,
  useScrollToggle,
  useScrollPositionToggle,
} from '@hua-labs/motion-core'

useScrollReveal

요소가 스크롤되어 뷰포트에 진입할 때 지정한 모션 타입으로 애니메이션을 트리거한다.

Signature

function useScrollReveal<T extends MotionElement = HTMLDivElement>(
  options?: ScrollRevealOptions
): BaseMotionReturn<T>

Parameters

파라미터타입기본값설명
motionTypeScrollRevealMotionType'fadeIn'애니메이션 타입
thresholdnumber프로필 기본값Intersection Observer 임계값 (0-1)
rootMarginstring'0px'IntersectionObserver rootMargin
triggerOnceboolean프로필 기본값한 번만 트리거
durationnumber프로필 기본값지속 시간 (ms)
delaynumber0지연 시간 (ms)
easingstring프로필 기본값CSS 이징

ScrollRevealMotionType

type ScrollRevealMotionType =
  | 'fadeIn'
  | 'slideUp'
  | 'slideLeft'
  | 'slideRight'
  | 'scaleIn'
  | 'bounceIn'

Usage

import { useScrollReveal } from '@hua-labs/motion-core'

function AnimatedSection() {
  const { ref, style } = useScrollReveal({
    motionType: 'slideUp',
    delay: 100,
    duration: 700
  })

  return <section ref={ref} style={style}>섹션 내용</section>
}

useScrollProgress

페이지 전체 스크롤 진행률을 0~100 사이의 숫자로 반환한다.

Signature

function useScrollProgress(options?: ScrollProgressOptions): {
  progress: number
  mounted: boolean
}

interface ScrollProgressOptions {
  target?: number   // 목표 스크롤 높이 (기본값: document 전체 높이)
  offset?: number   // 시작 오프셋 (px)
  showOnMount?: boolean
}

Returns

이름타입설명
progressnumber스크롤 진행률 (0~100)
mountedboolean클라이언트 마운트 여부

Usage

import { useScrollProgress } from '@hua-labs/motion-core'

function ProgressBar() {
  const { progress, mounted } = useScrollProgress()

  if (!mounted) return null

  return (
    <div
      style={{
        position: 'fixed',
        top: 0,
        left: 0,
        height: 3,
        width: `${progress}%`,
        background: 'cyan',
        transition: 'width 100ms linear'
      }}
    />
  )
}

useScrollDirection

현재 스크롤 방향을 감지한다. 네비게이션 바 숨김/표시 등에 활용한다.

Signature

function useScrollDirection(options?: ScrollDirectionConfig): {
  direction: ScrollDirection
  mounted: boolean
}

type ScrollDirection = 'up' | 'down' | 'idle'

interface ScrollDirectionConfig {
  threshold?: number    // 방향 변화를 감지할 최소 스크롤 거리 (기본값: 10)
  idleDelay?: number    // idle 상태로 전환 지연 시간 ms (기본값: 150)
  showOnMount?: boolean
}

Usage

import { useScrollDirection } from '@hua-labs/motion-core'

function Navbar() {
  const { direction, mounted } = useScrollDirection({ threshold: 5 })

  const isHidden = direction === 'down'

  return (
    <nav
      style={{
        transform: isHidden ? 'translateY(-100%)' : 'translateY(0)',
        transition: 'transform 300ms ease'
      }}
    >
      내비게이션
    </nav>
  )
}

useScrollToggle

요소의 스크롤 위치에 따라 표시/숨김 상태를 전환하며 애니메이션을 적용한다.

Signature

function useScrollToggle<T extends MotionElement = HTMLDivElement>(
  options?: ScrollToggleOptions
): BaseMotionReturn<T>

interface ScrollToggleOptions extends BaseMotionOptions {
  showScale?: number          // 표시 시 스케일 (기본값: 1)
  showOpacity?: number        // 표시 시 투명도 (기본값: 1)
  showRotate?: number         // 표시 시 회전 (deg, 기본값: 0)
  showTranslateY?: number     // 표시 시 Y 이동 (px, 기본값: 0)
  showTranslateX?: number     // 표시 시 X 이동 (px, 기본값: 0)
  hideScale?: number          // 숨김 시 스케일 (기본값: 0.8)
  hideOpacity?: number        // 숨김 시 투명도 (기본값: 0)
  hideRotate?: number         // 숨김 시 회전 (기본값: 0)
  hideTranslateY?: number     // 숨김 시 Y 이동 (기본값: 20)
  hideTranslateX?: number     // 숨김 시 X 이동 (기본값: 0)
  scrollThreshold?: number    // 임계값 비율 (0-1, 기본값: 0.1)
  scrollDirection?: 'up' | 'down' | 'both'  // 감지 방향 (기본값: 'both')
}

Usage

import { useScrollToggle } from '@hua-labs/motion-core'

function FloatingButton() {
  const { ref, style } = useScrollToggle({
    scrollThreshold: 0.3,
    scrollDirection: 'down',
    hideOpacity: 0,
    hideTranslateY: 30,
    duration: 300
  })

  return (
    <button ref={ref} style={style}>
      위로 가기
    </button>
  )
}

useScrollPositionToggle

window.pageYOffset가 설정한 임계값을 초과하면 가시성을 토글한다. "맨 위로 가기" 버튼 등에 활용한다.

Signature

function useScrollPositionToggle(options?: ScrollPositionToggleConfig): {
  isVisible: boolean
  scrollToTop: () => void
  mounted: boolean
}

interface ScrollPositionToggleConfig {
  threshold?: number    // 스크롤 임계값 (px, 기본값: 400)
  showOnMount?: boolean
  smooth?: boolean      // 부드러운 스크롤 여부 (기본값: true)
}

Usage

import { useScrollPositionToggle } from '@hua-labs/motion-core'

function BackToTop() {
  const { isVisible, scrollToTop, mounted } = useScrollPositionToggle({
    threshold: 300
  })

  if (!mounted || !isVisible) return null

  return (
    <button onClick={scrollToTop}>
      맨 위로
    </button>
  )
}

Notes

  • useScrollRevealsharedIntersectionObserver 풀을 재사용하여 다수의 요소를 관찰해도 Observer 인스턴스가 최소화된다.
  • useScrollProgressuseScrollToggle은 내부적으로 sharedScroll 구독 방식을 사용해 scroll 이벤트 리스너가 중복 등록되지 않는다.
  • useScrollDirectionmounted 상태를 확인한 후에 direction 값을 사용해야 SSR에서 안전하다.
  • useScrollPositionToggleuseScrollToggle은 목적이 다르다. useScrollPositionToggle은 절대 스크롤 위치 기준, useScrollToggle은 요소의 뷰포트 내 위치 기준이다.