Motion
유틸리티 훅
stable뷰포트 감지, 마우스 추적, 접근성, 윈도우 크기 추적 유틸리티 훅
@hua-labs/motion-corev2.0.0
Import
import {
useInView,
useMouse,
useReducedMotion,
useReducedMotionObject,
useWindowSize,
} from '@hua-labs/motion-core'useInView
Intersection Observer를 사용하여 요소가 뷰포트에 보이는지 감지한다. 스크롤 애니메이션, 레이지 로딩 등에 활용한다.
Signature
function useInView<T extends HTMLElement = HTMLDivElement>(
options?: InViewOptions
): InViewReturn<T>
interface InViewOptions {
threshold?: number | number[] // 기본값: 0
rootMargin?: string // 기본값: '0px'
triggerOnce?: boolean // 기본값: false
initialInView?: boolean // 기본값: false
}
interface InViewReturn<T extends HTMLElement = HTMLDivElement> {
ref: RefObject<T | null>
inView: boolean
entry: IntersectionObserverEntry | null
}Parameters
| 파라미터 | 타입 | 기본값 | 설명 | |
|---|---|---|---|---|
threshold | `number \ | number[]` | 0 | IntersectionObserver 임계값 |
rootMargin | string | '0px' | 루트 마진 (CSS margin 형식) | |
triggerOnce | boolean | false | 처음 감지 후 Observer 해제 | |
initialInView | boolean | false | 초기 가시성 상태 |
Returns
| 이름 | 타입 | 설명 | |
|---|---|---|---|
ref | RefObject<T> | 감지할 DOM 요소에 연결할 ref | |
inView | boolean | 현재 뷰포트에 보이는지 여부 | |
entry | `IntersectionObserverEntry \ | null` | 마지막 Observer 엔트리 |
Usage
import { useInView } from '@hua-labs/motion-core'
function LazyImage() {
const { ref, inView } = useInView({
threshold: 0.1,
triggerOnce: true
})
return (
<div ref={ref}>
{inView ? (
<img src="/photo.jpg" alt="사진" />
) : (
<div style={{ height: 300, background: '#f0f0f0' }} />
)}
</div>
)
}// 스크롤 애니메이션에 활용
function AnimatedSection() {
const { ref, inView } = useInView({ threshold: 0.2 })
return (
<section
ref={ref}
style={{
opacity: inView ? 1 : 0,
transform: inView ? 'translateY(0)' : 'translateY(20px)',
transition: 'all 600ms ease-out'
}}
>
섹션 내용
</section>
)
}useMouse
마우스 위치를 실시간으로 추적한다. 커서 따라다니는 효과, 마우스 기반 인터랙션 등에 활용한다.
Signature
function useMouse(options?: MouseOptions): MouseReturn
interface MouseOptions {
targetRef?: RefObject<HTMLElement | null> // 특정 요소 내 상대 좌표 계산용
throttle?: number // 업데이트 스로틀 (ms, 기본값: 0)
}
interface MouseReturn {
x: number // viewport 기준 X 좌표 (px)
y: number // viewport 기준 Y 좌표 (px)
elementX: number // 요소 내 상대 X 좌표 (0~1)
elementY: number // 요소 내 상대 Y 좌표 (0~1)
isOver: boolean // 마우스가 타겟 요소 위에 있는지 여부
}Parameters
| 파라미터 | 타입 | 기본값 | 설명 |
|---|---|---|---|
targetRef | RefObject<HTMLElement> | — | 상대 좌표를 계산할 기준 요소 |
throttle | number | 0 | 업데이트 스로틀 시간 (ms) |
targetRef를 지정하면 해당 요소 내에서의 상대 좌표(0~1)도 함께 계산한다. 지정하지 않으면 window 전체를 추적한다.
Usage
import { useRef } from 'react'
import { useMouse } from '@hua-labs/motion-core'
function MouseParallax() {
const containerRef = useRef<HTMLDivElement>(null)
const { elementX, elementY } = useMouse({
targetRef: containerRef,
throttle: 16 // ~60fps
})
const offsetX = (elementX - 0.5) * 20 // -10 ~ 10
const offsetY = (elementY - 0.5) * 20
return (
<div ref={containerRef} style={{ position: 'relative', overflow: 'hidden' }}>
<div
style={{
transform: `translate(${offsetX}px, ${offsetY}px)`,
transition: 'transform 100ms ease-out'
}}
>
패럴랙스 레이어
</div>
</div>
)
}// 전역 마우스 위치
function CursorTracker() {
const { x, y } = useMouse()
return (
<div
style={{
position: 'fixed',
left: x - 10,
top: y - 10,
width: 20,
height: 20,
borderRadius: '50%',
background: 'rgba(0,0,0,0.3)',
pointerEvents: 'none'
}}
/>
)
}useReducedMotion
사용자의 prefers-reduced-motion 접근성 설정을 감지한다. boolean을 직접 반환하는 가벼운 버전이다.
Signature
function useReducedMotion(): booleanReturns
true이면 사용자가 모션 감소를 선호하므로 애니메이션을 줄이거나 비활성화해야 한다.
Usage
import { useReducedMotion } from '@hua-labs/motion-core'
function AnimatedBanner() {
const prefersReducedMotion = useReducedMotion()
return (
<div
style={{
animation: prefersReducedMotion
? 'none'
: 'slideIn 600ms ease-out forwards'
}}
>
배너
</div>
)
}useReducedMotionObject
{ prefersReducedMotion: boolean } 객체 형태로 반환한다. 이전 버전과의 호환을 위해 유지된다.
Signature
function useReducedMotionObject(): { prefersReducedMotion: boolean }신규 코드에서는 useReducedMotion()을 사용하는 것을 권장한다.
Usage
import { useReducedMotionObject } from '@hua-labs/motion-core'
function LegacyComponent() {
const { prefersReducedMotion } = useReducedMotionObject()
return <div>{prefersReducedMotion ? '모션 없음' : '모션 있음'}</div>
}useWindowSize
브라우저 윈도우 크기 변경을 감지한다. 반응형 레이아웃, 리사이즈 기반 애니메이션 등에 활용한다.
Signature
function useWindowSize(options?: WindowSizeOptions): WindowSizeReturn
interface WindowSizeOptions {
debounce?: number // 디바운스 시간 (ms, 기본값: 100)
initialWidth?: number // 초기 너비 (SSR용, 기본값: 0)
initialHeight?: number // 초기 높이 (SSR용, 기본값: 0)
}
interface WindowSizeReturn {
width: number // 현재 윈도우 너비 (px)
height: number // 현재 윈도우 높이 (px)
isMounted: boolean // 클라이언트 마운트 여부
}Parameters
| 파라미터 | 타입 | 기본값 | 설명 |
|---|---|---|---|
debounce | number | 100 | resize 이벤트 디바운스 시간 (ms) |
initialWidth | number | 0 | SSR 초기 너비 |
initialHeight | number | 0 | SSR 초기 높이 |
Returns
| 이름 | 타입 | 설명 |
|---|---|---|
width | number | 윈도우 너비 (px) |
height | number | 윈도우 높이 (px) |
isMounted | boolean | 클라이언트 마운트 완료 여부 |
Usage
import { useWindowSize } from '@hua-labs/motion-core'
function ResponsiveComponent() {
const { width, height, isMounted } = useWindowSize({ debounce: 200 })
if (!isMounted) return null // SSR 대응
const isMobile = width < 768
return (
<div>
<p>뷰포트: {width} × {height}</p>
<p>{isMobile ? '모바일' : '데스크탑'}</p>
</div>
)
}Notes
useInView는 내부적으로sharedIntersectionObserver풀을 사용하므로 동일 요소에 여러 Observer가 등록되어도 인스턴스가 공유된다.useMouse의elementX,elementY는 0~1 범위로 클램프된다.useReducedMotion은 SSR에서 항상false를 반환한다. 클라이언트 마운트 후 실제 값으로 업데이트된다.useWindowSize의isMounted가false인 동안에는width와height가initialWidth/initialHeight값을 갖는다.