@hua-labs/motion-core 2.3.0 → 2.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +128 -39
- package/dist/chunk-47QAGQLN.mjs +1057 -0
- package/dist/chunk-47QAGQLN.mjs.map +1 -0
- package/dist/chunk-LSIP7MB5.cjs +1090 -0
- package/dist/chunk-LSIP7MB5.cjs.map +1 -0
- package/dist/index.cjs +7591 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.mts +637 -685
- package/dist/index.d.ts +1579 -0
- package/dist/index.mjs +2788 -1175
- package/dist/index.mjs.map +1 -1
- package/dist/native.cjs +692 -0
- package/dist/native.cjs.map +1 -0
- package/dist/native.d.mts +177 -0
- package/dist/native.d.ts +177 -0
- package/dist/native.mjs +555 -0
- package/dist/native.mjs.map +1 -0
- package/dist/springPhysics-BZVRi9PQ.d.mts +740 -0
- package/dist/springPhysics-BZVRi9PQ.d.ts +740 -0
- package/package.json +29 -9
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,1579 @@
|
|
|
1
|
+
import { P as PageType, a as PageMotionRef, b as PageMotionsConfig, B as BaseMotionOptions, E as EntranceType, M as MotionElement, c as BaseMotionReturn, F as FadeInOptions, d as EntranceMotionReturn, S as SlideOptions, e as ScaleOptions, f as BounceOptions, g as PulseOptions, h as SpringOptions, G as GradientOptions, H as HoverMotionOptions, i as ScrollRevealOptions, R as RepeatOptions, T as ToggleMotionOptions, I as InteractionReturn, j as InViewOptions, k as InViewReturn, l as MouseOptions, m as MouseReturn, n as ReducedMotionReturn, W as WindowSizeOptions, o as WindowSizeReturn, p as ScrollRevealMotionType } from './springPhysics-BZVRi9PQ.js';
|
|
2
|
+
export { A as AutoMotionReturn, q as BuiltInProfileName, D as DeepPartial, r as EasingFunction, s as EasingType, t as GestureConfig, u as GestureOptions, v as MOTION_PRESETS, w as MotionCallback, x as MotionConfig, y as MotionDirection, z as MotionEasing, C as MotionEngine, J as MotionFrame, K as MotionInstance, L as MotionOptions, N as MotionPreset, O as MotionProfile, Q as MotionProfileBase, U as MotionProfileEntrance, V as MotionProfileInteraction, X as MotionProfileProvider, Y as MotionProfileProviderProps, Z as MotionProfileSpring, _ as MotionProfileStagger, $ as MotionProgressCallback, a0 as MotionState, a1 as MotionStateCallback, a2 as MotionTrigger, a3 as MotionType, a4 as ObserverReturn, a5 as OrchestrationConfig, a6 as PAGE_MOTIONS, a7 as PageMotionElement, a8 as PerformanceMetrics, a9 as PresetConfig, aa as ReducedMotionStrategy, ab as SequenceOrchestrationConfig, ac as SpringConfig, ad as SpringPhysicsConfig, ae as SpringResult, af as StyleMotionReturn, ag as ToggleMotionReturn, ah as TransitionEffects, ai as TransitionOptions, aj as TransitionType, ak as applyEasing, al as calculateSpring, am as easeIn, an as easeInOut, ao as easeInOutQuad, ap as easeInQuad, aq as easeOut, ar as easeOutQuad, as as easingPresets, at as getAvailableEasings, au as getEasing, av as getMotionPreset, aw as getPagePreset, ax as getPresetEasing, ay as hua, az as isEasingFunction, aA as isValidEasing, aB as linear, aC as mergeProfileOverrides, aD as mergeWithPreset, aE as motionEngine, aF as neutral, aG as resolveProfile, aH as safeApplyEasing, aI as transitionEffects, aJ as useMotionProfile } from './springPhysics-BZVRi9PQ.js';
|
|
3
|
+
import * as react from 'react';
|
|
4
|
+
import react__default, { CSSProperties, RefObject } from 'react';
|
|
5
|
+
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* 1단계 API: 프리셋 기반 페이지 모션 (기존 방식)
|
|
9
|
+
*
|
|
10
|
+
* 사용법:
|
|
11
|
+
* ```typescript
|
|
12
|
+
* const motions = useSimplePageMotion('home')
|
|
13
|
+
* ```
|
|
14
|
+
*
|
|
15
|
+
* 지원하는 페이지 타입:
|
|
16
|
+
* - 'home': 홈페이지
|
|
17
|
+
* - 'dashboard': 대시보드
|
|
18
|
+
* - 'product': 제품 페이지
|
|
19
|
+
* - 'blog': 블로그
|
|
20
|
+
*/
|
|
21
|
+
declare function useSimplePageMotion(pageType: PageType): Record<string, PageMotionRef<HTMLElement>>;
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* 2단계 API: 페이지 레벨 모션 관리 (상태 관리자 버전)
|
|
25
|
+
*
|
|
26
|
+
* 사용법:
|
|
27
|
+
* ```typescript
|
|
28
|
+
* const motions = usePageMotions({
|
|
29
|
+
* hero: { type: 'hero' },
|
|
30
|
+
* title: { type: 'title' },
|
|
31
|
+
* button: { type: 'button', hover: true, click: true }
|
|
32
|
+
* })
|
|
33
|
+
* ```
|
|
34
|
+
*/
|
|
35
|
+
declare function usePageMotions(config: PageMotionsConfig): {
|
|
36
|
+
reset: () => void;
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
type MotionType = 'fadeIn' | 'slideUp' | 'slideLeft' | 'slideRight' | 'scaleIn' | 'bounceIn';
|
|
40
|
+
type ElementType = 'hero' | 'title' | 'button' | 'card' | 'text' | 'image';
|
|
41
|
+
interface SmartMotionOptions {
|
|
42
|
+
type?: ElementType;
|
|
43
|
+
entrance?: MotionType;
|
|
44
|
+
hover?: boolean;
|
|
45
|
+
click?: boolean;
|
|
46
|
+
delay?: number;
|
|
47
|
+
duration?: number;
|
|
48
|
+
threshold?: number;
|
|
49
|
+
autoLanguageSync?: boolean;
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* 3단계 API: 개별 요소 모션
|
|
53
|
+
*
|
|
54
|
+
* 사용법:
|
|
55
|
+
* ```typescript
|
|
56
|
+
* const heroRef = useSmartMotion({ type: 'hero' })
|
|
57
|
+
* const titleRef = useSmartMotion({ type: 'title' })
|
|
58
|
+
* const buttonRef = useSmartMotion({ type: 'button' })
|
|
59
|
+
* ```
|
|
60
|
+
*/
|
|
61
|
+
declare function useSmartMotion<T extends HTMLElement = HTMLDivElement>(options?: SmartMotionOptions): {
|
|
62
|
+
ref: React.RefObject<T | null>;
|
|
63
|
+
style: React.CSSProperties;
|
|
64
|
+
isVisible: boolean;
|
|
65
|
+
isHovered: boolean;
|
|
66
|
+
isClicked: boolean;
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* @hua-labs/motion-core - useUnifiedMotion
|
|
71
|
+
*
|
|
72
|
+
* 통합 Motion Hook - 단일 타입으로 여러 모션 효과 중 하나를 선택
|
|
73
|
+
* 내부에서 선택된 type에 맞는 로직만 실행 (6개 훅 동시 호출 제거)
|
|
74
|
+
*/
|
|
75
|
+
|
|
76
|
+
interface MotionEffects {
|
|
77
|
+
fade?: boolean | {
|
|
78
|
+
targetOpacity?: number;
|
|
79
|
+
};
|
|
80
|
+
slide?: boolean | {
|
|
81
|
+
direction?: 'up' | 'down' | 'left' | 'right';
|
|
82
|
+
distance?: number;
|
|
83
|
+
};
|
|
84
|
+
scale?: boolean | {
|
|
85
|
+
from?: number;
|
|
86
|
+
to?: number;
|
|
87
|
+
};
|
|
88
|
+
bounce?: boolean;
|
|
89
|
+
}
|
|
90
|
+
interface UseUnifiedMotionOptions extends Omit<BaseMotionOptions, 'autoStart'> {
|
|
91
|
+
/** Motion type to use (single effect mode) */
|
|
92
|
+
type?: EntranceType;
|
|
93
|
+
/** Multiple effects to combine (multi-effect mode) */
|
|
94
|
+
effects?: MotionEffects;
|
|
95
|
+
/** Auto start animation @default true */
|
|
96
|
+
autoStart?: boolean;
|
|
97
|
+
/** Slide distance (px) for slide types @default 50 */
|
|
98
|
+
distance?: number;
|
|
99
|
+
}
|
|
100
|
+
declare function useUnifiedMotion<T extends MotionElement = HTMLDivElement>(options: UseUnifiedMotionOptions): BaseMotionReturn<T>;
|
|
101
|
+
|
|
102
|
+
declare function useFadeIn<T extends MotionElement = HTMLDivElement>(options?: FadeInOptions): EntranceMotionReturn<T>;
|
|
103
|
+
|
|
104
|
+
declare function useSlideUp<T extends MotionElement = HTMLDivElement>(options?: SlideOptions): EntranceMotionReturn<T>;
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* useSlideLeft - 오른쪽에서 왼쪽으로 슬라이드하며 나타나는 애니메이션 훅
|
|
108
|
+
*
|
|
109
|
+
* useSlideUp의 wrapper로, direction: 'left'를 기본값으로 사용합니다.
|
|
110
|
+
* IntersectionObserver를 통해 뷰포트 진입 시 자동으로 애니메이션이 시작됩니다.
|
|
111
|
+
*
|
|
112
|
+
* @example
|
|
113
|
+
* ```tsx
|
|
114
|
+
* const slideLeft = useSlideLeft({ duration: 700, distance: 50 });
|
|
115
|
+
*
|
|
116
|
+
* return (
|
|
117
|
+
* <div ref={slideLeft.ref} style={slideLeft.style}>
|
|
118
|
+
* Content slides in from right
|
|
119
|
+
* </div>
|
|
120
|
+
* );
|
|
121
|
+
* ```
|
|
122
|
+
*/
|
|
123
|
+
declare function useSlideLeft<T extends MotionElement = HTMLDivElement>(options?: Omit<SlideOptions, "direction">): EntranceMotionReturn<T>;
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* useSlideRight - 왼쪽에서 오른쪽으로 슬라이드하며 나타나는 애니메이션 훅
|
|
127
|
+
*
|
|
128
|
+
* useSlideUp의 wrapper로, direction: 'right'를 기본값으로 사용합니다.
|
|
129
|
+
* IntersectionObserver를 통해 뷰포트 진입 시 자동으로 애니메이션이 시작됩니다.
|
|
130
|
+
*
|
|
131
|
+
* @example
|
|
132
|
+
* ```tsx
|
|
133
|
+
* const slideRight = useSlideRight({ duration: 700, distance: 50 });
|
|
134
|
+
*
|
|
135
|
+
* return (
|
|
136
|
+
* <div ref={slideRight.ref} style={slideRight.style}>
|
|
137
|
+
* Content slides in from left
|
|
138
|
+
* </div>
|
|
139
|
+
* );
|
|
140
|
+
* ```
|
|
141
|
+
*/
|
|
142
|
+
declare function useSlideRight<T extends MotionElement = HTMLDivElement>(options?: Omit<SlideOptions, "direction">): EntranceMotionReturn<T>;
|
|
143
|
+
|
|
144
|
+
declare function useScaleIn<T extends MotionElement = HTMLDivElement>(options?: ScaleOptions): EntranceMotionReturn<T>;
|
|
145
|
+
|
|
146
|
+
declare function useBounceIn<T extends MotionElement = HTMLDivElement>(options?: BounceOptions): EntranceMotionReturn<T>;
|
|
147
|
+
|
|
148
|
+
declare function usePulse<T extends MotionElement = HTMLDivElement>(options?: PulseOptions): BaseMotionReturn<T>;
|
|
149
|
+
|
|
150
|
+
interface SpringMotionOptions extends SpringOptions {
|
|
151
|
+
/** 시작 값 */
|
|
152
|
+
from: number;
|
|
153
|
+
/** 목표 값 */
|
|
154
|
+
to: number;
|
|
155
|
+
/** 활성화 여부 */
|
|
156
|
+
enabled?: boolean;
|
|
157
|
+
}
|
|
158
|
+
declare function useSpringMotion<T extends MotionElement = HTMLDivElement>(options: SpringMotionOptions): BaseMotionReturn<T> & {
|
|
159
|
+
value: number;
|
|
160
|
+
velocity: number;
|
|
161
|
+
};
|
|
162
|
+
|
|
163
|
+
declare function useGradient<T extends MotionElement = HTMLDivElement>(options?: GradientOptions): BaseMotionReturn<T>;
|
|
164
|
+
|
|
165
|
+
declare function useHoverMotion<T extends MotionElement = HTMLDivElement>(options?: HoverMotionOptions): BaseMotionReturn<T> & {
|
|
166
|
+
isHovered: boolean;
|
|
167
|
+
};
|
|
168
|
+
|
|
169
|
+
interface ClickToggleOptions {
|
|
170
|
+
initialState?: boolean;
|
|
171
|
+
toggleOnClick?: boolean;
|
|
172
|
+
toggleOnDoubleClick?: boolean;
|
|
173
|
+
toggleOnRightClick?: boolean;
|
|
174
|
+
toggleOnEnter?: boolean;
|
|
175
|
+
toggleOnSpace?: boolean;
|
|
176
|
+
autoReset?: boolean;
|
|
177
|
+
resetDelay?: number;
|
|
178
|
+
preventDefault?: boolean;
|
|
179
|
+
stopPropagation?: boolean;
|
|
180
|
+
showOnMount?: boolean;
|
|
181
|
+
}
|
|
182
|
+
interface ClickToggleReturn {
|
|
183
|
+
isActive: boolean;
|
|
184
|
+
mounted: boolean;
|
|
185
|
+
toggle: () => void;
|
|
186
|
+
activate: () => void;
|
|
187
|
+
deactivate: () => void;
|
|
188
|
+
reset: () => void;
|
|
189
|
+
clickHandlers: {
|
|
190
|
+
onClick?: (event: React.MouseEvent) => void;
|
|
191
|
+
onDoubleClick?: (event: React.MouseEvent) => void;
|
|
192
|
+
onContextMenu?: (event: React.MouseEvent) => void;
|
|
193
|
+
onKeyDown?: (event: React.KeyboardEvent) => void;
|
|
194
|
+
};
|
|
195
|
+
}
|
|
196
|
+
declare function useClickToggle(options?: ClickToggleOptions): ClickToggleReturn;
|
|
197
|
+
|
|
198
|
+
interface FocusToggleOptions {
|
|
199
|
+
initialState?: boolean;
|
|
200
|
+
toggleOnFocus?: boolean;
|
|
201
|
+
toggleOnBlur?: boolean;
|
|
202
|
+
toggleOnFocusIn?: boolean;
|
|
203
|
+
toggleOnFocusOut?: boolean;
|
|
204
|
+
autoReset?: boolean;
|
|
205
|
+
resetDelay?: number;
|
|
206
|
+
preventDefault?: boolean;
|
|
207
|
+
stopPropagation?: boolean;
|
|
208
|
+
showOnMount?: boolean;
|
|
209
|
+
}
|
|
210
|
+
interface FocusToggleReturn {
|
|
211
|
+
isActive: boolean;
|
|
212
|
+
mounted: boolean;
|
|
213
|
+
toggle: () => void;
|
|
214
|
+
activate: () => void;
|
|
215
|
+
deactivate: () => void;
|
|
216
|
+
reset: () => void;
|
|
217
|
+
focusHandlers: {
|
|
218
|
+
onFocus?: (event: React.FocusEvent) => void;
|
|
219
|
+
onBlur?: (event: React.FocusEvent) => void;
|
|
220
|
+
onFocusIn?: (event: React.FocusEvent) => void;
|
|
221
|
+
onFocusOut?: (event: React.FocusEvent) => void;
|
|
222
|
+
};
|
|
223
|
+
ref: React.RefObject<HTMLElement | null>;
|
|
224
|
+
}
|
|
225
|
+
declare function useFocusToggle(options?: FocusToggleOptions): FocusToggleReturn;
|
|
226
|
+
|
|
227
|
+
declare function useScrollReveal<T extends MotionElement = HTMLDivElement>(options?: ScrollRevealOptions): BaseMotionReturn<T>;
|
|
228
|
+
|
|
229
|
+
interface ScrollProgressOptions {
|
|
230
|
+
target?: number;
|
|
231
|
+
offset?: number;
|
|
232
|
+
showOnMount?: boolean;
|
|
233
|
+
}
|
|
234
|
+
declare function useScrollProgress(options?: ScrollProgressOptions): {
|
|
235
|
+
progress: number;
|
|
236
|
+
mounted: boolean;
|
|
237
|
+
};
|
|
238
|
+
|
|
239
|
+
type MotionState = 'idle' | 'playing' | 'paused' | 'completed' | 'error';
|
|
240
|
+
type MotionDirection = 'forward' | 'reverse' | 'alternate';
|
|
241
|
+
interface MotionStateOptions {
|
|
242
|
+
initialState?: MotionState;
|
|
243
|
+
autoStart?: boolean;
|
|
244
|
+
loop?: boolean;
|
|
245
|
+
direction?: MotionDirection;
|
|
246
|
+
duration?: number;
|
|
247
|
+
delay?: number;
|
|
248
|
+
showOnMount?: boolean;
|
|
249
|
+
}
|
|
250
|
+
interface MotionStateReturn {
|
|
251
|
+
state: MotionState;
|
|
252
|
+
direction: MotionDirection;
|
|
253
|
+
progress: number;
|
|
254
|
+
elapsed: number;
|
|
255
|
+
remaining: number;
|
|
256
|
+
mounted: boolean;
|
|
257
|
+
play: () => void;
|
|
258
|
+
pause: () => void;
|
|
259
|
+
stop: () => void;
|
|
260
|
+
reset: () => void;
|
|
261
|
+
reverse: () => void;
|
|
262
|
+
seek: (progress: number) => void;
|
|
263
|
+
setState: (newState: MotionState) => void;
|
|
264
|
+
}
|
|
265
|
+
declare function useMotionState(options?: MotionStateOptions): MotionStateReturn;
|
|
266
|
+
|
|
267
|
+
declare function useRepeat<T extends MotionElement = HTMLDivElement>(options?: RepeatOptions): BaseMotionReturn<T> & {
|
|
268
|
+
isAnimating: boolean;
|
|
269
|
+
};
|
|
270
|
+
|
|
271
|
+
declare function useToggleMotion<T extends MotionElement = HTMLDivElement>(options?: ToggleMotionOptions): InteractionReturn<T>;
|
|
272
|
+
|
|
273
|
+
/**
|
|
274
|
+
* useSlideDown - 아래에서 위로 슬라이드하며 나타나는 애니메이션 훅
|
|
275
|
+
*
|
|
276
|
+
* useSlideUp의 wrapper로, direction: 'down'을 기본값으로 사용합니다.
|
|
277
|
+
* IntersectionObserver를 통해 뷰포트 진입 시 자동으로 애니메이션이 시작됩니다.
|
|
278
|
+
*
|
|
279
|
+
* @example
|
|
280
|
+
* ```tsx
|
|
281
|
+
* const slideDown = useSlideDown({ duration: 700, distance: 50 });
|
|
282
|
+
*
|
|
283
|
+
* return (
|
|
284
|
+
* <div ref={slideDown.ref} style={slideDown.style}>
|
|
285
|
+
* Content slides down into view
|
|
286
|
+
* </div>
|
|
287
|
+
* );
|
|
288
|
+
* ```
|
|
289
|
+
*/
|
|
290
|
+
declare function useSlideDown<T extends MotionElement = HTMLDivElement>(options?: Omit<SlideOptions, "direction">): EntranceMotionReturn<T>;
|
|
291
|
+
|
|
292
|
+
/**
|
|
293
|
+
* useInView - 요소의 뷰포트 가시성 감지 훅
|
|
294
|
+
* Viewport visibility detection hook
|
|
295
|
+
*
|
|
296
|
+
* @description
|
|
297
|
+
* Intersection Observer를 사용하여 요소가 뷰포트에 보이는지 감지.
|
|
298
|
+
* 스크롤 애니메이션, 레이지 로딩 등에 활용.
|
|
299
|
+
* Detects whether an element is visible in the viewport using Intersection Observer.
|
|
300
|
+
* Useful for scroll animations, lazy loading, etc.
|
|
301
|
+
*
|
|
302
|
+
* @example
|
|
303
|
+
* ```tsx
|
|
304
|
+
* const { ref, inView } = useInView({ threshold: 0.5, triggerOnce: true })
|
|
305
|
+
*
|
|
306
|
+
* return (
|
|
307
|
+
* <div ref={ref} style={{ opacity: inView ? 1 : 0 }}>
|
|
308
|
+
* {inView ? 'Visible!' : 'Not visible'}
|
|
309
|
+
* </div>
|
|
310
|
+
* )
|
|
311
|
+
* ```
|
|
312
|
+
*/
|
|
313
|
+
declare function useInView<T extends HTMLElement = HTMLDivElement>(options?: InViewOptions): InViewReturn<T>;
|
|
314
|
+
|
|
315
|
+
/**
|
|
316
|
+
* useMouse - 마우스 위치 추적 훅
|
|
317
|
+
* Mouse position tracking hook
|
|
318
|
+
*
|
|
319
|
+
* @description
|
|
320
|
+
* 마우스 위치를 실시간으로 추적. 커서 따라다니는 효과,
|
|
321
|
+
* 마우스 기반 인터랙션 등에 활용.
|
|
322
|
+
* Tracks mouse position in real-time. Useful for cursor-following effects
|
|
323
|
+
* and mouse-based interactions.
|
|
324
|
+
*
|
|
325
|
+
* @example
|
|
326
|
+
* ```tsx
|
|
327
|
+
* const { x, y, elementX, elementY } = useMouse()
|
|
328
|
+
*
|
|
329
|
+
* return (
|
|
330
|
+
* <div style={{
|
|
331
|
+
* '--mouse-x': elementX,
|
|
332
|
+
* '--mouse-y': elementY
|
|
333
|
+
* }}>
|
|
334
|
+
* Mouse: {x}, {y}
|
|
335
|
+
* </div>
|
|
336
|
+
* )
|
|
337
|
+
* ```
|
|
338
|
+
*/
|
|
339
|
+
declare function useMouse(options?: MouseOptions): MouseReturn;
|
|
340
|
+
|
|
341
|
+
/**
|
|
342
|
+
* useReducedMotion - 모션 감소 설정 감지 훅
|
|
343
|
+
* Reduced motion preference detection hook
|
|
344
|
+
*
|
|
345
|
+
* @description
|
|
346
|
+
* 사용자의 prefers-reduced-motion 설정을 감지.
|
|
347
|
+
* 접근성을 위해 모션을 줄이거나 비활성화할 때 사용.
|
|
348
|
+
* Detects user's prefers-reduced-motion setting.
|
|
349
|
+
* Used to reduce or disable motion for accessibility.
|
|
350
|
+
*
|
|
351
|
+
* @returns {boolean} 모션 감소 선호 여부 / Whether reduced motion is preferred
|
|
352
|
+
*
|
|
353
|
+
* @example
|
|
354
|
+
* ```tsx
|
|
355
|
+
* const prefersReducedMotion = useReducedMotion()
|
|
356
|
+
*
|
|
357
|
+
* return (
|
|
358
|
+
* <motion.div
|
|
359
|
+
* animate={prefersReducedMotion ? {} : { scale: [1, 1.1, 1] }}
|
|
360
|
+
* transition={{ duration: prefersReducedMotion ? 0 : 0.3 }}
|
|
361
|
+
* >
|
|
362
|
+
* Accessible Motion
|
|
363
|
+
* </motion.div>
|
|
364
|
+
* )
|
|
365
|
+
* ```
|
|
366
|
+
*/
|
|
367
|
+
declare function useReducedMotion(): boolean;
|
|
368
|
+
/**
|
|
369
|
+
* useReducedMotionObject - 객체 형태 반환 (backwards compat)
|
|
370
|
+
* Returns `{ prefersReducedMotion: boolean }` for backwards compatibility.
|
|
371
|
+
*
|
|
372
|
+
* @deprecated Use `useReducedMotion()` which returns `boolean` directly.
|
|
373
|
+
*
|
|
374
|
+
* @example
|
|
375
|
+
* ```tsx
|
|
376
|
+
* const { prefersReducedMotion } = useReducedMotionObject()
|
|
377
|
+
* ```
|
|
378
|
+
*/
|
|
379
|
+
declare function useReducedMotionObject(): ReducedMotionReturn;
|
|
380
|
+
|
|
381
|
+
/**
|
|
382
|
+
* useWindowSize - 윈도우 크기 추적 훅
|
|
383
|
+
* Window size tracking hook
|
|
384
|
+
*
|
|
385
|
+
* @description
|
|
386
|
+
* 브라우저 윈도우 크기 변경을 감지.
|
|
387
|
+
* 반응형 레이아웃, 리사이즈 기반 애니메이션 등에 활용.
|
|
388
|
+
* Detects browser window size changes.
|
|
389
|
+
* Useful for responsive layouts and resize-based animations.
|
|
390
|
+
*
|
|
391
|
+
* @example
|
|
392
|
+
* ```tsx
|
|
393
|
+
* const { width, height, isMounted } = useWindowSize({ debounce: 100 })
|
|
394
|
+
*
|
|
395
|
+
* return (
|
|
396
|
+
* <div>
|
|
397
|
+
* {isMounted ? `${width} x ${height}` : 'Loading...'}
|
|
398
|
+
* </div>
|
|
399
|
+
* )
|
|
400
|
+
* ```
|
|
401
|
+
*/
|
|
402
|
+
declare function useWindowSize(options?: WindowSizeOptions): WindowSizeReturn;
|
|
403
|
+
|
|
404
|
+
interface GestureOptions {
|
|
405
|
+
enabled?: boolean;
|
|
406
|
+
threshold?: number;
|
|
407
|
+
timeout?: number;
|
|
408
|
+
swipeThreshold?: number;
|
|
409
|
+
swipeVelocity?: number;
|
|
410
|
+
swipeDirections?: ('up' | 'down' | 'left' | 'right')[];
|
|
411
|
+
pinchThreshold?: number;
|
|
412
|
+
minScale?: number;
|
|
413
|
+
maxScale?: number;
|
|
414
|
+
rotateThreshold?: number;
|
|
415
|
+
panThreshold?: number;
|
|
416
|
+
onSwipe?: (direction: 'up' | 'down' | 'left' | 'right', distance: number, velocity: number) => void;
|
|
417
|
+
onPinch?: (scale: number, delta: number) => void;
|
|
418
|
+
onRotate?: (angle: number, delta: number) => void;
|
|
419
|
+
onPan?: (deltaX: number, deltaY: number, totalX: number, totalY: number) => void;
|
|
420
|
+
onTap?: (x: number, y: number) => void;
|
|
421
|
+
onDoubleTap?: (x: number, y: number) => void;
|
|
422
|
+
onLongPress?: (x: number, y: number) => void;
|
|
423
|
+
onStart?: (x: number, y: number) => void;
|
|
424
|
+
onMove?: (x: number, y: number) => void;
|
|
425
|
+
onEnd?: (x: number, y: number) => void;
|
|
426
|
+
}
|
|
427
|
+
interface GestureReturn {
|
|
428
|
+
isActive: boolean;
|
|
429
|
+
gesture: string | null;
|
|
430
|
+
scale: number;
|
|
431
|
+
rotation: number;
|
|
432
|
+
deltaX: number;
|
|
433
|
+
deltaY: number;
|
|
434
|
+
distance: number;
|
|
435
|
+
velocity: number;
|
|
436
|
+
start: () => void;
|
|
437
|
+
stop: () => void;
|
|
438
|
+
reset: () => void;
|
|
439
|
+
onTouchStart: (e: React.TouchEvent | TouchEvent) => void;
|
|
440
|
+
onTouchMove: (e: React.TouchEvent | TouchEvent) => void;
|
|
441
|
+
onTouchEnd: (e: React.TouchEvent | TouchEvent) => void;
|
|
442
|
+
onMouseDown: (e: React.MouseEvent | MouseEvent) => void;
|
|
443
|
+
onMouseMove: (e: React.MouseEvent | MouseEvent) => void;
|
|
444
|
+
onMouseUp: (e: React.MouseEvent | MouseEvent) => void;
|
|
445
|
+
}
|
|
446
|
+
declare function useGesture(options?: GestureOptions): GestureReturn;
|
|
447
|
+
|
|
448
|
+
interface GestureMotionOptions {
|
|
449
|
+
gestureType: 'hover' | 'drag' | 'pinch' | 'swipe' | 'tilt';
|
|
450
|
+
duration?: number;
|
|
451
|
+
easing?: string;
|
|
452
|
+
sensitivity?: number;
|
|
453
|
+
enabled?: boolean;
|
|
454
|
+
onGestureStart?: () => void;
|
|
455
|
+
onGestureEnd?: () => void;
|
|
456
|
+
}
|
|
457
|
+
interface GestureState {
|
|
458
|
+
isActive: boolean;
|
|
459
|
+
x: number;
|
|
460
|
+
y: number;
|
|
461
|
+
deltaX: number;
|
|
462
|
+
deltaY: number;
|
|
463
|
+
scale: number;
|
|
464
|
+
rotation: number;
|
|
465
|
+
}
|
|
466
|
+
declare function useGestureMotion(options: GestureMotionOptions): {
|
|
467
|
+
ref: react.RefObject<HTMLElement | null>;
|
|
468
|
+
gestureState: GestureState;
|
|
469
|
+
motionStyle: react.CSSProperties;
|
|
470
|
+
isActive: boolean;
|
|
471
|
+
};
|
|
472
|
+
|
|
473
|
+
interface ButtonEffectOptions extends BaseMotionOptions {
|
|
474
|
+
type?: 'scale' | 'ripple' | 'glow' | 'shake' | 'bounce' | 'slide' | 'custom';
|
|
475
|
+
scaleAmount?: number;
|
|
476
|
+
rippleColor?: string;
|
|
477
|
+
rippleSize?: number;
|
|
478
|
+
rippleDuration?: number;
|
|
479
|
+
glowColor?: string;
|
|
480
|
+
glowSize?: number;
|
|
481
|
+
glowIntensity?: number;
|
|
482
|
+
shakeAmount?: number;
|
|
483
|
+
shakeFrequency?: number;
|
|
484
|
+
bounceHeight?: number;
|
|
485
|
+
bounceStiffness?: number;
|
|
486
|
+
slideDistance?: number;
|
|
487
|
+
slideDirection?: 'left' | 'right' | 'up' | 'down';
|
|
488
|
+
hoverScale?: number;
|
|
489
|
+
hoverRotate?: number;
|
|
490
|
+
hoverTranslateY?: number;
|
|
491
|
+
hoverTranslateX?: number;
|
|
492
|
+
activeScale?: number;
|
|
493
|
+
activeRotate?: number;
|
|
494
|
+
activeTranslateY?: number;
|
|
495
|
+
activeTranslateX?: number;
|
|
496
|
+
focusScale?: number;
|
|
497
|
+
focusGlow?: boolean;
|
|
498
|
+
disabled?: boolean;
|
|
499
|
+
disabledOpacity?: number;
|
|
500
|
+
autoStart?: boolean;
|
|
501
|
+
}
|
|
502
|
+
declare function useButtonEffect<T extends MotionElement = HTMLButtonElement>(options?: ButtonEffectOptions): BaseMotionReturn<T> & {
|
|
503
|
+
buttonType: string;
|
|
504
|
+
isPressed: boolean;
|
|
505
|
+
isHovered: boolean;
|
|
506
|
+
isFocused: boolean;
|
|
507
|
+
ripplePosition: {
|
|
508
|
+
x: number;
|
|
509
|
+
y: number;
|
|
510
|
+
};
|
|
511
|
+
currentGlowIntensity: number;
|
|
512
|
+
shakeOffset: number;
|
|
513
|
+
bounceOffset: number;
|
|
514
|
+
slideOffset: number;
|
|
515
|
+
pressButton: () => void;
|
|
516
|
+
releaseButton: () => void;
|
|
517
|
+
setButtonState: (state: 'idle' | 'hover' | 'active' | 'focus' | 'disabled') => void;
|
|
518
|
+
};
|
|
519
|
+
|
|
520
|
+
interface VisibilityToggleOptions extends BaseMotionOptions {
|
|
521
|
+
showScale?: number;
|
|
522
|
+
showOpacity?: number;
|
|
523
|
+
showRotate?: number;
|
|
524
|
+
showTranslateY?: number;
|
|
525
|
+
showTranslateX?: number;
|
|
526
|
+
hideScale?: number;
|
|
527
|
+
hideOpacity?: number;
|
|
528
|
+
hideRotate?: number;
|
|
529
|
+
hideTranslateY?: number;
|
|
530
|
+
hideTranslateX?: number;
|
|
531
|
+
}
|
|
532
|
+
declare function useVisibilityToggle<T extends MotionElement = HTMLDivElement>(options?: VisibilityToggleOptions): InteractionReturn<T>;
|
|
533
|
+
|
|
534
|
+
interface ScrollToggleOptions extends BaseMotionOptions {
|
|
535
|
+
showScale?: number;
|
|
536
|
+
showOpacity?: number;
|
|
537
|
+
showRotate?: number;
|
|
538
|
+
showTranslateY?: number;
|
|
539
|
+
showTranslateX?: number;
|
|
540
|
+
hideScale?: number;
|
|
541
|
+
hideOpacity?: number;
|
|
542
|
+
hideRotate?: number;
|
|
543
|
+
hideTranslateY?: number;
|
|
544
|
+
hideTranslateX?: number;
|
|
545
|
+
scrollThreshold?: number;
|
|
546
|
+
scrollDirection?: 'up' | 'down' | 'both';
|
|
547
|
+
}
|
|
548
|
+
declare function useScrollToggle<T extends MotionElement = HTMLDivElement>(options?: ScrollToggleOptions): BaseMotionReturn<T>;
|
|
549
|
+
|
|
550
|
+
interface CardListOptions extends BaseMotionOptions {
|
|
551
|
+
staggerDelay?: number;
|
|
552
|
+
cardScale?: number;
|
|
553
|
+
cardOpacity?: number;
|
|
554
|
+
cardRotate?: number;
|
|
555
|
+
cardTranslateY?: number;
|
|
556
|
+
cardTranslateX?: number;
|
|
557
|
+
initialScale?: number;
|
|
558
|
+
initialOpacity?: number;
|
|
559
|
+
initialRotate?: number;
|
|
560
|
+
initialTranslateY?: number;
|
|
561
|
+
initialTranslateX?: number;
|
|
562
|
+
gridColumns?: number;
|
|
563
|
+
gridGap?: number;
|
|
564
|
+
}
|
|
565
|
+
declare function useCardList<T extends MotionElement = HTMLDivElement>(options?: CardListOptions): BaseMotionReturn<T> & {
|
|
566
|
+
cardStyles: React.CSSProperties[];
|
|
567
|
+
staggerDelay: number;
|
|
568
|
+
gridColumns: number;
|
|
569
|
+
gridGap: number;
|
|
570
|
+
};
|
|
571
|
+
|
|
572
|
+
interface LoadingSpinnerOptions extends BaseMotionOptions {
|
|
573
|
+
type?: 'rotate' | 'pulse' | 'bounce' | 'wave' | 'dots' | 'bars' | 'custom';
|
|
574
|
+
rotationSpeed?: number;
|
|
575
|
+
pulseSpeed?: number;
|
|
576
|
+
bounceHeight?: number;
|
|
577
|
+
waveCount?: number;
|
|
578
|
+
dotCount?: number;
|
|
579
|
+
barCount?: number;
|
|
580
|
+
color?: string;
|
|
581
|
+
backgroundColor?: string;
|
|
582
|
+
size?: number;
|
|
583
|
+
thickness?: number;
|
|
584
|
+
autoStart?: boolean;
|
|
585
|
+
infinite?: boolean;
|
|
586
|
+
}
|
|
587
|
+
declare function useLoadingSpinner<T extends MotionElement = HTMLDivElement>(options?: LoadingSpinnerOptions): BaseMotionReturn<T> & {
|
|
588
|
+
isLoading: boolean;
|
|
589
|
+
spinnerType: string;
|
|
590
|
+
rotationAngle: number;
|
|
591
|
+
pulseScale: number;
|
|
592
|
+
bounceOffset: number;
|
|
593
|
+
waveProgress: number;
|
|
594
|
+
dotProgress: number;
|
|
595
|
+
barProgress: number;
|
|
596
|
+
startLoading: () => void;
|
|
597
|
+
stopLoading: () => void;
|
|
598
|
+
setLoadingState: (loading: boolean) => void;
|
|
599
|
+
};
|
|
600
|
+
|
|
601
|
+
interface NavigationOptions extends BaseMotionOptions {
|
|
602
|
+
type?: 'slide' | 'fade' | 'scale' | 'rotate' | 'custom';
|
|
603
|
+
slideDirection?: 'left' | 'right' | 'up' | 'down';
|
|
604
|
+
staggerDelay?: number;
|
|
605
|
+
itemScale?: number;
|
|
606
|
+
itemOpacity?: number;
|
|
607
|
+
itemRotate?: number;
|
|
608
|
+
itemTranslateY?: number;
|
|
609
|
+
itemTranslateX?: number;
|
|
610
|
+
initialScale?: number;
|
|
611
|
+
initialOpacity?: number;
|
|
612
|
+
initialRotate?: number;
|
|
613
|
+
initialTranslateY?: number;
|
|
614
|
+
initialTranslateX?: number;
|
|
615
|
+
activeScale?: number;
|
|
616
|
+
activeOpacity?: number;
|
|
617
|
+
activeRotate?: number;
|
|
618
|
+
activeTranslateY?: number;
|
|
619
|
+
activeTranslateX?: number;
|
|
620
|
+
hoverScale?: number;
|
|
621
|
+
hoverOpacity?: number;
|
|
622
|
+
hoverRotate?: number;
|
|
623
|
+
hoverTranslateY?: number;
|
|
624
|
+
hoverTranslateX?: number;
|
|
625
|
+
itemCount?: number;
|
|
626
|
+
autoStart?: boolean;
|
|
627
|
+
}
|
|
628
|
+
declare function useNavigation<T extends MotionElement = HTMLDivElement>(options?: NavigationOptions): BaseMotionReturn<T> & {
|
|
629
|
+
isOpen: boolean;
|
|
630
|
+
activeIndex: number;
|
|
631
|
+
itemStyles: React.CSSProperties[];
|
|
632
|
+
openMenu: () => void;
|
|
633
|
+
closeMenu: () => void;
|
|
634
|
+
toggleMenu: () => void;
|
|
635
|
+
setActiveItem: (index: number) => void;
|
|
636
|
+
goToNext: () => void;
|
|
637
|
+
goToPrevious: () => void;
|
|
638
|
+
};
|
|
639
|
+
|
|
640
|
+
interface SkeletonOptions extends BaseMotionOptions {
|
|
641
|
+
/** 스켈레톤 배경색 */
|
|
642
|
+
backgroundColor?: string;
|
|
643
|
+
/** 스켈레톤 하이라이트 색상 */
|
|
644
|
+
highlightColor?: string;
|
|
645
|
+
/** 모션 속도 (ms) */
|
|
646
|
+
motionSpeed?: number;
|
|
647
|
+
/** 스켈레톤 높이 */
|
|
648
|
+
height?: number;
|
|
649
|
+
/** 스켈레톤 너비 */
|
|
650
|
+
width?: number | string;
|
|
651
|
+
/** 보더 반지름 */
|
|
652
|
+
borderRadius?: number;
|
|
653
|
+
/** 웨이브 모션 활성화 여부 */
|
|
654
|
+
wave?: boolean;
|
|
655
|
+
/** 펄스 모션 활성화 여부 */
|
|
656
|
+
pulse?: boolean;
|
|
657
|
+
}
|
|
658
|
+
declare function useSkeleton<T extends MotionElement = HTMLDivElement>(options?: SkeletonOptions): BaseMotionReturn<T>;
|
|
659
|
+
|
|
660
|
+
interface TypewriterOptions {
|
|
661
|
+
/** 타이핑할 텍스트 */
|
|
662
|
+
text: string;
|
|
663
|
+
/** 글자당 딜레이 (ms) @default 50 */
|
|
664
|
+
speed?: number;
|
|
665
|
+
/** 시작 딜레이 (ms) @default 0 */
|
|
666
|
+
delay?: number;
|
|
667
|
+
/** 활성화 여부 @default true */
|
|
668
|
+
enabled?: boolean;
|
|
669
|
+
/** 완료 콜백 */
|
|
670
|
+
onComplete?: () => void;
|
|
671
|
+
}
|
|
672
|
+
interface TypewriterReturn {
|
|
673
|
+
/** 현재 표시 중인 텍스트 */
|
|
674
|
+
displayText: string;
|
|
675
|
+
/** 타이핑 진행 중 여부 */
|
|
676
|
+
isTyping: boolean;
|
|
677
|
+
/** 진행률 (0-1) */
|
|
678
|
+
progress: number;
|
|
679
|
+
/** 타이핑 재시작 */
|
|
680
|
+
restart: () => void;
|
|
681
|
+
}
|
|
682
|
+
/**
|
|
683
|
+
* useTypewriter - 타이핑 효과 훅
|
|
684
|
+
*
|
|
685
|
+
* @example
|
|
686
|
+
* ```tsx
|
|
687
|
+
* const { displayText, isTyping } = useTypewriter({ text: 'Hello, world!' })
|
|
688
|
+
* return <h1>{displayText}<span className="animate-pulse">|</span></h1>
|
|
689
|
+
* ```
|
|
690
|
+
*/
|
|
691
|
+
declare function useTypewriter(options: TypewriterOptions): TypewriterReturn;
|
|
692
|
+
|
|
693
|
+
interface CustomCursorOptions {
|
|
694
|
+
/** 활성화 여부 @default true */
|
|
695
|
+
enabled?: boolean;
|
|
696
|
+
/** 커서 크기 (px) @default 32 */
|
|
697
|
+
size?: number;
|
|
698
|
+
/** 스프링 스무딩 (0-1, 낮을수록 부드러움) @default 0.15 */
|
|
699
|
+
smoothing?: number;
|
|
700
|
+
/** 호버 시 스케일 @default 1.5 */
|
|
701
|
+
hoverScale?: number;
|
|
702
|
+
/** data-cursor 속성 요소 감지 @default true */
|
|
703
|
+
detectLabels?: boolean;
|
|
704
|
+
}
|
|
705
|
+
interface CustomCursorReturn {
|
|
706
|
+
/** 커서 x 좌표 (smoothed) */
|
|
707
|
+
x: number;
|
|
708
|
+
/** 커서 y 좌표 (smoothed) */
|
|
709
|
+
y: number;
|
|
710
|
+
/** 호버 중인 요소의 data-cursor 값 */
|
|
711
|
+
label: string | null;
|
|
712
|
+
/** 호버 상태 여부 */
|
|
713
|
+
isHovering: boolean;
|
|
714
|
+
/** 커서 스타일 (CSS variables 포함) */
|
|
715
|
+
style: CSSProperties;
|
|
716
|
+
/** 커서 가시 여부 */
|
|
717
|
+
isVisible: boolean;
|
|
718
|
+
}
|
|
719
|
+
/**
|
|
720
|
+
* useCustomCursor - 커스텀 커서 효과 훅
|
|
721
|
+
*
|
|
722
|
+
* 마우스를 따라다니는 커스텀 커서를 구현합니다.
|
|
723
|
+
* 스프링 보간으로 부드러운 추적, data-cursor 라벨 감지.
|
|
724
|
+
*
|
|
725
|
+
* @example
|
|
726
|
+
* ```tsx
|
|
727
|
+
* const cursor = useCustomCursor()
|
|
728
|
+
* return (
|
|
729
|
+
* <>
|
|
730
|
+
* <div style={cursor.style} className="custom-cursor" />
|
|
731
|
+
* <button data-cursor="Click me">Hover me</button>
|
|
732
|
+
* </>
|
|
733
|
+
* )
|
|
734
|
+
* ```
|
|
735
|
+
*/
|
|
736
|
+
declare function useCustomCursor(options?: CustomCursorOptions): CustomCursorReturn;
|
|
737
|
+
|
|
738
|
+
interface MagneticCursorOptions {
|
|
739
|
+
/** 자석 끌림 강도 (0-1) @default 0.3 */
|
|
740
|
+
strength?: number;
|
|
741
|
+
/** 자석 작동 반경 (px) @default 100 */
|
|
742
|
+
radius?: number;
|
|
743
|
+
/** 활성화 여부 @default true */
|
|
744
|
+
enabled?: boolean;
|
|
745
|
+
}
|
|
746
|
+
interface MagneticCursorReturn<T extends HTMLElement = HTMLElement> {
|
|
747
|
+
/** 자석 대상 요소에 연결할 ref */
|
|
748
|
+
ref: React.RefObject<T | null>;
|
|
749
|
+
/** 마우스 이벤트 핸들러 */
|
|
750
|
+
handlers: {
|
|
751
|
+
onMouseMove: (e: React.MouseEvent) => void;
|
|
752
|
+
onMouseLeave: () => void;
|
|
753
|
+
};
|
|
754
|
+
/** 요소에 적용할 transform 스타일 */
|
|
755
|
+
style: CSSProperties;
|
|
756
|
+
}
|
|
757
|
+
/**
|
|
758
|
+
* useMagneticCursor - 자석 커서 효과 훅
|
|
759
|
+
*
|
|
760
|
+
* 마우스가 요소 근처에 오면 요소가 마우스 쪽으로 끌려오는 효과.
|
|
761
|
+
* 버튼, 아이콘 등에 적용하면 인터랙티브 느낌 향상.
|
|
762
|
+
*
|
|
763
|
+
* @example
|
|
764
|
+
* ```tsx
|
|
765
|
+
* const magnetic = useMagneticCursor<HTMLButtonElement>({ strength: 0.4 })
|
|
766
|
+
* return <button ref={magnetic.ref} style={magnetic.style} {...magnetic.handlers}>Click</button>
|
|
767
|
+
* ```
|
|
768
|
+
*/
|
|
769
|
+
declare function useMagneticCursor<T extends HTMLElement = HTMLElement>(options?: MagneticCursorOptions): MagneticCursorReturn<T>;
|
|
770
|
+
|
|
771
|
+
interface SmoothScrollOptions {
|
|
772
|
+
/** 활성화 여부 @default true */
|
|
773
|
+
enabled?: boolean;
|
|
774
|
+
/** 스무딩 계수 (0-1, 낮을수록 부드러움) @default 0.1 */
|
|
775
|
+
lerp?: number;
|
|
776
|
+
/** 마우스 휠 배율 @default 1 */
|
|
777
|
+
wheelMultiplier?: number;
|
|
778
|
+
/** 터치 배율 @default 2 */
|
|
779
|
+
touchMultiplier?: number;
|
|
780
|
+
/** 방향 @default 'vertical' */
|
|
781
|
+
direction?: 'vertical' | 'horizontal';
|
|
782
|
+
/** 콜백: 스크롤 값 변경 시 */
|
|
783
|
+
onScroll?: (scroll: number) => void;
|
|
784
|
+
}
|
|
785
|
+
interface SmoothScrollReturn {
|
|
786
|
+
/** 현재 스크롤 위치 (smoothed) */
|
|
787
|
+
scroll: number;
|
|
788
|
+
/** 목표 스크롤 위치 */
|
|
789
|
+
targetScroll: number;
|
|
790
|
+
/** 스크롤 진행률 (0-1) */
|
|
791
|
+
progress: number;
|
|
792
|
+
/** 특정 위치로 스크롤 */
|
|
793
|
+
scrollTo: (target: number | HTMLElement, options?: {
|
|
794
|
+
offset?: number;
|
|
795
|
+
}) => void;
|
|
796
|
+
/** 스크롤 중지 */
|
|
797
|
+
stop: () => void;
|
|
798
|
+
}
|
|
799
|
+
/**
|
|
800
|
+
* useSmoothScroll - 부드러운 스크롤 훅
|
|
801
|
+
*
|
|
802
|
+
* lenis 스타일의 smooth scrolling. 네이티브 스크롤을 가로채서
|
|
803
|
+
* lerp 보간으로 부드러운 스크롤 경험을 제공합니다.
|
|
804
|
+
*
|
|
805
|
+
* @example
|
|
806
|
+
* ```tsx
|
|
807
|
+
* const { scroll, progress, scrollTo } = useSmoothScroll({ lerp: 0.08 })
|
|
808
|
+
* return <button onClick={() => scrollTo(document.getElementById('section')!)}>Go</button>
|
|
809
|
+
* ```
|
|
810
|
+
*/
|
|
811
|
+
declare function useSmoothScroll(options?: SmoothScrollOptions): SmoothScrollReturn;
|
|
812
|
+
|
|
813
|
+
interface ElementProgressOptions {
|
|
814
|
+
/** 요소가 뷰포트 아래에서 시작되는 위치 (0 = 하단, 1 = 상단) @default 0 */
|
|
815
|
+
start?: number;
|
|
816
|
+
/** 요소가 뷰포트 위에서 끝나는 위치 @default 1 */
|
|
817
|
+
end?: number;
|
|
818
|
+
/** 클램프 여부 @default true */
|
|
819
|
+
clamp?: boolean;
|
|
820
|
+
}
|
|
821
|
+
interface ElementProgressReturn<T extends HTMLElement = HTMLElement> {
|
|
822
|
+
/** 대상 요소 ref */
|
|
823
|
+
ref: React.RefObject<T | null>;
|
|
824
|
+
/** 요소의 스크롤 진행률 (0-1) */
|
|
825
|
+
progress: number;
|
|
826
|
+
/** 요소가 뷰포트 안에 있는지 */
|
|
827
|
+
isInView: boolean;
|
|
828
|
+
}
|
|
829
|
+
/**
|
|
830
|
+
* useElementProgress - 요소 단위 스크롤 진행률 훅
|
|
831
|
+
*
|
|
832
|
+
* useScrollProgress의 확장판. 페이지 전체가 아니라
|
|
833
|
+
* 특정 요소가 뷰포트를 통과하는 진행률을 추적합니다.
|
|
834
|
+
*
|
|
835
|
+
* 공유 스크롤 리스너(subscribeScroll)를 사용해 N개 인스턴스도
|
|
836
|
+
* 단 하나의 scroll/resize 이벤트 + rAF 배칭으로 처리됩니다.
|
|
837
|
+
*
|
|
838
|
+
* @example
|
|
839
|
+
* ```tsx
|
|
840
|
+
* const { ref, progress, isInView } = useElementProgress<HTMLDivElement>()
|
|
841
|
+
* return (
|
|
842
|
+
* <div ref={ref} style={{ opacity: progress }}>
|
|
843
|
+
* Fades in as you scroll
|
|
844
|
+
* </div>
|
|
845
|
+
* )
|
|
846
|
+
* ```
|
|
847
|
+
*/
|
|
848
|
+
declare function useElementProgress<T extends HTMLElement = HTMLElement>(options?: ElementProgressOptions): ElementProgressReturn<T>;
|
|
849
|
+
|
|
850
|
+
/**
|
|
851
|
+
* Shared IntersectionObserver Pool
|
|
852
|
+
*
|
|
853
|
+
* threshold|rootMargin 조합별로 하나의 IntersectionObserver를 공유합니다.
|
|
854
|
+
* N개 훅 = K개 observer (K = 고유 옵션 조합 수, 보통 1~3).
|
|
855
|
+
*
|
|
856
|
+
* sharedScroll.ts와 동일한 singleton pool 패턴.
|
|
857
|
+
*
|
|
858
|
+
* @example
|
|
859
|
+
* ```ts
|
|
860
|
+
* useEffect(() => {
|
|
861
|
+
* if (!ref.current) return
|
|
862
|
+
* return observeElement(
|
|
863
|
+
* ref.current,
|
|
864
|
+
* (entry) => { if (entry.isIntersecting) start() },
|
|
865
|
+
* { threshold: 0.1 },
|
|
866
|
+
* true // once
|
|
867
|
+
* )
|
|
868
|
+
* }, [])
|
|
869
|
+
* ```
|
|
870
|
+
*/
|
|
871
|
+
type EntryCallback = (entry: IntersectionObserverEntry) => void;
|
|
872
|
+
/**
|
|
873
|
+
* element를 공유 IntersectionObserver에 등록.
|
|
874
|
+
* 반환 함수를 호출하면 구독 해제.
|
|
875
|
+
*
|
|
876
|
+
* @param element - 관찰할 DOM element
|
|
877
|
+
* @param callback - intersection 변화 시 호출되는 콜백 (해당 element의 entry만 전달)
|
|
878
|
+
* @param options - threshold, rootMargin
|
|
879
|
+
* @param once - true이면 첫 intersection 후 자동 unsubscribe
|
|
880
|
+
* @returns unsubscribe 함수
|
|
881
|
+
*/
|
|
882
|
+
declare function observeElement(element: Element, callback: EntryCallback, options?: {
|
|
883
|
+
threshold?: number | number[];
|
|
884
|
+
rootMargin?: string;
|
|
885
|
+
}, once?: boolean): () => void;
|
|
886
|
+
|
|
887
|
+
interface AutoFadeConfig {
|
|
888
|
+
initialOpacity?: number;
|
|
889
|
+
targetOpacity?: number;
|
|
890
|
+
duration?: number;
|
|
891
|
+
delay?: number;
|
|
892
|
+
repeat?: boolean;
|
|
893
|
+
repeatDelay?: number;
|
|
894
|
+
repeatCount?: number;
|
|
895
|
+
ease?: "linear" | "ease-in" | "ease-out" | "ease-in-out";
|
|
896
|
+
autoStart?: boolean;
|
|
897
|
+
onComplete?: () => void;
|
|
898
|
+
onRepeat?: (count: number) => void;
|
|
899
|
+
showOnMount?: boolean;
|
|
900
|
+
}
|
|
901
|
+
interface AutoFadeReturn {
|
|
902
|
+
opacity: number;
|
|
903
|
+
isAnimating: boolean;
|
|
904
|
+
isVisible: boolean;
|
|
905
|
+
mounted: boolean;
|
|
906
|
+
start: () => void;
|
|
907
|
+
stop: () => void;
|
|
908
|
+
reset: () => void;
|
|
909
|
+
fadeIn: () => void;
|
|
910
|
+
fadeOut: () => void;
|
|
911
|
+
toggle: () => void;
|
|
912
|
+
}
|
|
913
|
+
declare function useAutoFade(options?: AutoFadeConfig): AutoFadeReturn;
|
|
914
|
+
|
|
915
|
+
interface AutoPlayConfig {
|
|
916
|
+
interval?: number;
|
|
917
|
+
delay?: number;
|
|
918
|
+
repeat?: number | "infinite";
|
|
919
|
+
autoStart?: boolean;
|
|
920
|
+
pauseOnHover?: boolean;
|
|
921
|
+
pauseOnBlur?: boolean;
|
|
922
|
+
showOnMount?: boolean;
|
|
923
|
+
}
|
|
924
|
+
declare function useAutoPlay(options?: AutoPlayConfig): {
|
|
925
|
+
isPlaying: boolean;
|
|
926
|
+
isPaused: boolean;
|
|
927
|
+
currentStep: number;
|
|
928
|
+
mounted: boolean;
|
|
929
|
+
start: () => void;
|
|
930
|
+
stop: () => void;
|
|
931
|
+
pause: () => void;
|
|
932
|
+
resume: () => void;
|
|
933
|
+
next: () => void;
|
|
934
|
+
previous: () => void;
|
|
935
|
+
goTo: (step: number) => void;
|
|
936
|
+
};
|
|
937
|
+
|
|
938
|
+
interface AutoScaleConfig {
|
|
939
|
+
initialScale?: number;
|
|
940
|
+
targetScale?: number;
|
|
941
|
+
duration?: number;
|
|
942
|
+
delay?: number;
|
|
943
|
+
repeat?: boolean;
|
|
944
|
+
repeatDelay?: number;
|
|
945
|
+
repeatCount?: number;
|
|
946
|
+
ease?: "linear" | "ease-in" | "ease-out" | "ease-in-out" | "bounce" | "elastic";
|
|
947
|
+
autoStart?: boolean;
|
|
948
|
+
onComplete?: () => void;
|
|
949
|
+
onRepeat?: (count: number) => void;
|
|
950
|
+
showOnMount?: boolean;
|
|
951
|
+
centerTransform?: boolean;
|
|
952
|
+
}
|
|
953
|
+
interface AutoScaleReturn {
|
|
954
|
+
scale: number;
|
|
955
|
+
isAnimating: boolean;
|
|
956
|
+
isVisible: boolean;
|
|
957
|
+
mounted: boolean;
|
|
958
|
+
start: () => void;
|
|
959
|
+
stop: () => void;
|
|
960
|
+
reset: () => void;
|
|
961
|
+
scaleIn: () => void;
|
|
962
|
+
scaleOut: () => void;
|
|
963
|
+
toggle: () => void;
|
|
964
|
+
}
|
|
965
|
+
declare function useAutoScale(options?: AutoScaleConfig): AutoScaleReturn;
|
|
966
|
+
|
|
967
|
+
type SlideDirection = "left" | "right" | "up" | "down" | "left-up" | "left-down" | "right-up" | "right-down";
|
|
968
|
+
interface AutoSlideConfig {
|
|
969
|
+
direction?: SlideDirection;
|
|
970
|
+
distance?: number;
|
|
971
|
+
initialPosition?: {
|
|
972
|
+
x: number;
|
|
973
|
+
y: number;
|
|
974
|
+
};
|
|
975
|
+
targetPosition?: {
|
|
976
|
+
x: number;
|
|
977
|
+
y: number;
|
|
978
|
+
};
|
|
979
|
+
duration?: number;
|
|
980
|
+
delay?: number;
|
|
981
|
+
repeat?: boolean;
|
|
982
|
+
repeatDelay?: number;
|
|
983
|
+
repeatCount?: number;
|
|
984
|
+
ease?: "linear" | "ease-in" | "ease-out" | "ease-in-out";
|
|
985
|
+
autoStart?: boolean;
|
|
986
|
+
onComplete?: () => void;
|
|
987
|
+
onRepeat?: (count: number) => void;
|
|
988
|
+
showOnMount?: boolean;
|
|
989
|
+
}
|
|
990
|
+
interface AutoSlideReturn {
|
|
991
|
+
position: {
|
|
992
|
+
x: number;
|
|
993
|
+
y: number;
|
|
994
|
+
};
|
|
995
|
+
isAnimating: boolean;
|
|
996
|
+
isVisible: boolean;
|
|
997
|
+
mounted: boolean;
|
|
998
|
+
start: () => void;
|
|
999
|
+
stop: () => void;
|
|
1000
|
+
reset: () => void;
|
|
1001
|
+
slideIn: () => void;
|
|
1002
|
+
slideOut: () => void;
|
|
1003
|
+
toggle: () => void;
|
|
1004
|
+
}
|
|
1005
|
+
declare function useAutoSlide(options?: AutoSlideConfig): AutoSlideReturn;
|
|
1006
|
+
|
|
1007
|
+
interface MotionStep$1 {
|
|
1008
|
+
id: string;
|
|
1009
|
+
motion: () => void;
|
|
1010
|
+
delay?: number;
|
|
1011
|
+
duration?: number;
|
|
1012
|
+
onComplete?: () => void;
|
|
1013
|
+
}
|
|
1014
|
+
interface MotionOrchestraOptions {
|
|
1015
|
+
mode?: "sequential" | "parallel" | "stagger";
|
|
1016
|
+
staggerDelay?: number;
|
|
1017
|
+
autoStart?: boolean;
|
|
1018
|
+
loop?: boolean;
|
|
1019
|
+
onComplete?: () => void;
|
|
1020
|
+
}
|
|
1021
|
+
declare function useMotionOrchestra(options?: MotionOrchestraOptions): {
|
|
1022
|
+
addMotion: (step: MotionStep$1) => void;
|
|
1023
|
+
removeMotion: (id: string) => void;
|
|
1024
|
+
play: () => void;
|
|
1025
|
+
stop: () => void;
|
|
1026
|
+
pause: () => void;
|
|
1027
|
+
resume: () => void;
|
|
1028
|
+
isPlaying: boolean;
|
|
1029
|
+
currentStep: number;
|
|
1030
|
+
completedSteps: Set<string>;
|
|
1031
|
+
totalSteps: number;
|
|
1032
|
+
};
|
|
1033
|
+
|
|
1034
|
+
interface MotionStep {
|
|
1035
|
+
id: string;
|
|
1036
|
+
duration: number;
|
|
1037
|
+
delay?: number;
|
|
1038
|
+
ease?: "linear" | "ease-in" | "ease-out" | "ease-in-out" | "bounce" | "elastic";
|
|
1039
|
+
onStart?: () => void;
|
|
1040
|
+
onUpdate?: (progress: number) => void;
|
|
1041
|
+
onComplete?: () => void;
|
|
1042
|
+
onError?: (error: Error) => void;
|
|
1043
|
+
}
|
|
1044
|
+
interface OrchestrationConfig {
|
|
1045
|
+
autoStart?: boolean;
|
|
1046
|
+
loop?: boolean;
|
|
1047
|
+
loopCount?: number;
|
|
1048
|
+
loopDelay?: number;
|
|
1049
|
+
timeline?: MotionStep[];
|
|
1050
|
+
duration?: number;
|
|
1051
|
+
speed?: number;
|
|
1052
|
+
reverse?: boolean;
|
|
1053
|
+
onStart?: () => void;
|
|
1054
|
+
onComplete?: () => void;
|
|
1055
|
+
onLoop?: (count: number) => void;
|
|
1056
|
+
onError?: (error: Error) => void;
|
|
1057
|
+
onProgress?: (progress: number) => void;
|
|
1058
|
+
onStepStart?: (stepId: string) => void;
|
|
1059
|
+
onStepComplete?: (stepId: string) => void;
|
|
1060
|
+
}
|
|
1061
|
+
interface OrchestrationReturn {
|
|
1062
|
+
isPlaying: boolean;
|
|
1063
|
+
isPaused: boolean;
|
|
1064
|
+
currentTime: number;
|
|
1065
|
+
progress: number;
|
|
1066
|
+
currentStep: string | null;
|
|
1067
|
+
loopCount: number;
|
|
1068
|
+
error: string | null;
|
|
1069
|
+
play: () => void;
|
|
1070
|
+
pause: () => void;
|
|
1071
|
+
stop: () => void;
|
|
1072
|
+
reset: () => void;
|
|
1073
|
+
seek: (time: number) => void;
|
|
1074
|
+
setSpeed: (speed: number) => void;
|
|
1075
|
+
reverse: () => void;
|
|
1076
|
+
addStep: (step: MotionStep) => void;
|
|
1077
|
+
removeStep: (stepId: string) => void;
|
|
1078
|
+
updateStep: (stepId: string, updates: Partial<MotionStep>) => void;
|
|
1079
|
+
reorderSteps: (stepIds: string[]) => void;
|
|
1080
|
+
getStepProgress: (stepId: string) => number;
|
|
1081
|
+
getStepTime: (stepId: string) => number;
|
|
1082
|
+
getTotalDuration: () => number;
|
|
1083
|
+
}
|
|
1084
|
+
declare function useOrchestration(options?: OrchestrationConfig): OrchestrationReturn;
|
|
1085
|
+
|
|
1086
|
+
/** The subset of BaseMotionReturn that useSequence actually consumes. */
|
|
1087
|
+
type SequenceMotionResult = Pick<BaseMotionReturn<MotionElement>, "start" | "stop" | "reset" | "ref"> & {
|
|
1088
|
+
pause?: () => void;
|
|
1089
|
+
resume?: () => void;
|
|
1090
|
+
};
|
|
1091
|
+
interface SequenceConfig {
|
|
1092
|
+
autoStart?: boolean;
|
|
1093
|
+
loop?: boolean;
|
|
1094
|
+
}
|
|
1095
|
+
/** @deprecated Use useOrchestration or useMotionOrchestra instead. */
|
|
1096
|
+
interface SequenceItem {
|
|
1097
|
+
hook: () => SequenceMotionResult;
|
|
1098
|
+
delay?: number;
|
|
1099
|
+
}
|
|
1100
|
+
/** @deprecated Use useOrchestration or useMotionOrchestra instead. */
|
|
1101
|
+
declare function useSequence(sequence: SequenceItem[], options?: SequenceConfig): {
|
|
1102
|
+
start: () => void;
|
|
1103
|
+
stop: () => void;
|
|
1104
|
+
pause: () => void;
|
|
1105
|
+
resume: () => void;
|
|
1106
|
+
reset: () => void;
|
|
1107
|
+
isPlaying: boolean;
|
|
1108
|
+
currentIndex: number;
|
|
1109
|
+
totalMotions: number;
|
|
1110
|
+
ref: react.RefObject<MotionElement | null>;
|
|
1111
|
+
};
|
|
1112
|
+
|
|
1113
|
+
interface LayoutMotionConfig {
|
|
1114
|
+
from: {
|
|
1115
|
+
width?: number | string;
|
|
1116
|
+
height?: number | string;
|
|
1117
|
+
flexDirection?: "row" | "column" | "row-reverse" | "column-reverse";
|
|
1118
|
+
justifyContent?: "flex-start" | "flex-end" | "center" | "space-between" | "space-around" | "space-evenly";
|
|
1119
|
+
alignItems?: "flex-start" | "flex-end" | "center" | "stretch" | "baseline";
|
|
1120
|
+
gap?: number | string;
|
|
1121
|
+
gridTemplateColumns?: string;
|
|
1122
|
+
gridTemplateRows?: string;
|
|
1123
|
+
gridGap?: number | string;
|
|
1124
|
+
};
|
|
1125
|
+
to: {
|
|
1126
|
+
width?: number | string;
|
|
1127
|
+
height?: number | string;
|
|
1128
|
+
flexDirection?: "row" | "column" | "row-reverse" | "column-reverse";
|
|
1129
|
+
justifyContent?: "flex-start" | "flex-end" | "center" | "space-between" | "space-around" | "space-evenly";
|
|
1130
|
+
alignItems?: "flex-start" | "flex-end" | "center" | "stretch" | "baseline";
|
|
1131
|
+
gap?: number | string;
|
|
1132
|
+
gridTemplateColumns?: string;
|
|
1133
|
+
gridTemplateRows?: string;
|
|
1134
|
+
gridGap?: number | string;
|
|
1135
|
+
};
|
|
1136
|
+
duration?: number;
|
|
1137
|
+
easing?: string;
|
|
1138
|
+
autoStart?: boolean;
|
|
1139
|
+
onComplete?: () => void;
|
|
1140
|
+
}
|
|
1141
|
+
interface LayoutState {
|
|
1142
|
+
isAnimating: boolean;
|
|
1143
|
+
progress: number;
|
|
1144
|
+
currentStyle: React.CSSProperties;
|
|
1145
|
+
}
|
|
1146
|
+
declare function useLayoutMotion(config: LayoutMotionConfig): {
|
|
1147
|
+
ref: react.RefObject<HTMLDivElement | null>;
|
|
1148
|
+
state: LayoutState;
|
|
1149
|
+
start: () => void;
|
|
1150
|
+
stop: () => void;
|
|
1151
|
+
reset: () => void;
|
|
1152
|
+
};
|
|
1153
|
+
declare function createLayoutTransition(from: LayoutMotionConfig["from"], to: LayoutMotionConfig["to"], options?: Partial<LayoutMotionConfig>): {
|
|
1154
|
+
from: {
|
|
1155
|
+
width?: number | string;
|
|
1156
|
+
height?: number | string;
|
|
1157
|
+
flexDirection?: "row" | "column" | "row-reverse" | "column-reverse";
|
|
1158
|
+
justifyContent?: "flex-start" | "flex-end" | "center" | "space-between" | "space-around" | "space-evenly";
|
|
1159
|
+
alignItems?: "flex-start" | "flex-end" | "center" | "stretch" | "baseline";
|
|
1160
|
+
gap?: number | string;
|
|
1161
|
+
gridTemplateColumns?: string;
|
|
1162
|
+
gridTemplateRows?: string;
|
|
1163
|
+
gridGap?: number | string;
|
|
1164
|
+
};
|
|
1165
|
+
to: {
|
|
1166
|
+
width?: number | string;
|
|
1167
|
+
height?: number | string;
|
|
1168
|
+
flexDirection?: "row" | "column" | "row-reverse" | "column-reverse";
|
|
1169
|
+
justifyContent?: "flex-start" | "flex-end" | "center" | "space-between" | "space-around" | "space-evenly";
|
|
1170
|
+
alignItems?: "flex-start" | "flex-end" | "center" | "stretch" | "baseline";
|
|
1171
|
+
gap?: number | string;
|
|
1172
|
+
gridTemplateColumns?: string;
|
|
1173
|
+
gridTemplateRows?: string;
|
|
1174
|
+
gridGap?: number | string;
|
|
1175
|
+
};
|
|
1176
|
+
duration: number;
|
|
1177
|
+
easing: string;
|
|
1178
|
+
autoStart: boolean;
|
|
1179
|
+
onComplete: (() => void) | undefined;
|
|
1180
|
+
};
|
|
1181
|
+
|
|
1182
|
+
interface KeyboardToggleConfig {
|
|
1183
|
+
initialState?: boolean;
|
|
1184
|
+
keys?: string[];
|
|
1185
|
+
keyCode?: number;
|
|
1186
|
+
keyCombo?: string[];
|
|
1187
|
+
toggleOnKeyDown?: boolean;
|
|
1188
|
+
toggleOnKeyUp?: boolean;
|
|
1189
|
+
toggleOnKeyPress?: boolean;
|
|
1190
|
+
autoReset?: boolean;
|
|
1191
|
+
resetDelay?: number;
|
|
1192
|
+
preventDefault?: boolean;
|
|
1193
|
+
stopPropagation?: boolean;
|
|
1194
|
+
requireFocus?: boolean;
|
|
1195
|
+
showOnMount?: boolean;
|
|
1196
|
+
}
|
|
1197
|
+
interface KeyboardToggleReturn {
|
|
1198
|
+
isActive: boolean;
|
|
1199
|
+
mounted: boolean;
|
|
1200
|
+
toggle: () => void;
|
|
1201
|
+
activate: () => void;
|
|
1202
|
+
deactivate: () => void;
|
|
1203
|
+
reset: () => void;
|
|
1204
|
+
keyboardHandlers: {
|
|
1205
|
+
onKeyDown?: (event: React.KeyboardEvent) => void;
|
|
1206
|
+
onKeyUp?: (event: React.KeyboardEvent) => void;
|
|
1207
|
+
onKeyPress?: (event: React.KeyboardEvent) => void;
|
|
1208
|
+
};
|
|
1209
|
+
ref: React.RefObject<HTMLElement | null>;
|
|
1210
|
+
}
|
|
1211
|
+
declare function useKeyboardToggle(options?: KeyboardToggleConfig): KeyboardToggleReturn;
|
|
1212
|
+
|
|
1213
|
+
type ScrollDirection = "up" | "down" | "idle";
|
|
1214
|
+
interface ScrollDirectionConfig {
|
|
1215
|
+
threshold?: number;
|
|
1216
|
+
idleDelay?: number;
|
|
1217
|
+
showOnMount?: boolean;
|
|
1218
|
+
}
|
|
1219
|
+
declare function useScrollDirection(options?: ScrollDirectionConfig): {
|
|
1220
|
+
direction: ScrollDirection;
|
|
1221
|
+
mounted: boolean;
|
|
1222
|
+
};
|
|
1223
|
+
|
|
1224
|
+
interface StickyToggleConfig {
|
|
1225
|
+
offset?: number;
|
|
1226
|
+
behavior?: "smooth" | "auto";
|
|
1227
|
+
showOnMount?: boolean;
|
|
1228
|
+
}
|
|
1229
|
+
declare function useStickyToggle(options?: StickyToggleConfig): {
|
|
1230
|
+
isSticky: boolean;
|
|
1231
|
+
mounted: boolean;
|
|
1232
|
+
};
|
|
1233
|
+
|
|
1234
|
+
interface InteractiveConfig {
|
|
1235
|
+
hoverScale?: number;
|
|
1236
|
+
clickScale?: number;
|
|
1237
|
+
duration?: number;
|
|
1238
|
+
}
|
|
1239
|
+
interface InteractiveState {
|
|
1240
|
+
scale: number;
|
|
1241
|
+
isHovered: boolean;
|
|
1242
|
+
isClicked: boolean;
|
|
1243
|
+
}
|
|
1244
|
+
declare function useInteractive(config?: InteractiveConfig): {
|
|
1245
|
+
ref: (element: HTMLDivElement | null) => void;
|
|
1246
|
+
scale: number;
|
|
1247
|
+
isHovered: boolean;
|
|
1248
|
+
isClicked: boolean;
|
|
1249
|
+
handleMouseEnter: () => void;
|
|
1250
|
+
handleMouseLeave: () => void;
|
|
1251
|
+
handleMouseDown: () => void;
|
|
1252
|
+
handleMouseUp: () => void;
|
|
1253
|
+
};
|
|
1254
|
+
|
|
1255
|
+
interface PerformanceMonitorConfig {
|
|
1256
|
+
threshold?: number;
|
|
1257
|
+
onPerformanceIssue?: (fps: number) => void;
|
|
1258
|
+
}
|
|
1259
|
+
interface PerformanceMonitorState {
|
|
1260
|
+
fps: number;
|
|
1261
|
+
isLowPerformance: boolean;
|
|
1262
|
+
frameCount: number;
|
|
1263
|
+
}
|
|
1264
|
+
declare function usePerformanceMonitor(config?: PerformanceMonitorConfig): {
|
|
1265
|
+
ref: (element: HTMLDivElement | null) => void;
|
|
1266
|
+
fps: number;
|
|
1267
|
+
isLowPerformance: boolean;
|
|
1268
|
+
frameCount: number;
|
|
1269
|
+
};
|
|
1270
|
+
|
|
1271
|
+
interface LanguageConfig {
|
|
1272
|
+
motionType: "fadeIn" | "slideUp" | "slideLeft" | "slideRight" | "scaleIn" | "bounceIn";
|
|
1273
|
+
duration?: number;
|
|
1274
|
+
delay?: number;
|
|
1275
|
+
threshold?: number;
|
|
1276
|
+
pauseOnLanguageChange?: boolean;
|
|
1277
|
+
restartOnLanguageChange?: boolean;
|
|
1278
|
+
currentLanguage?: string;
|
|
1279
|
+
}
|
|
1280
|
+
declare function useLanguageAwareMotion(options: LanguageConfig): {
|
|
1281
|
+
ref: react.RefObject<HTMLElement | null>;
|
|
1282
|
+
isVisible: boolean;
|
|
1283
|
+
isPaused: boolean;
|
|
1284
|
+
style: {
|
|
1285
|
+
opacity: number;
|
|
1286
|
+
transform: string;
|
|
1287
|
+
transition: string;
|
|
1288
|
+
} | {
|
|
1289
|
+
opacity: number;
|
|
1290
|
+
transition: string;
|
|
1291
|
+
transform?: undefined;
|
|
1292
|
+
};
|
|
1293
|
+
pauseMotion: () => void;
|
|
1294
|
+
resumeMotion: () => void;
|
|
1295
|
+
restartMotion: () => void;
|
|
1296
|
+
currentLanguage: string;
|
|
1297
|
+
};
|
|
1298
|
+
|
|
1299
|
+
interface GameLoopConfig {
|
|
1300
|
+
fps?: number;
|
|
1301
|
+
autoStart?: boolean;
|
|
1302
|
+
maxFPS?: number;
|
|
1303
|
+
minFPS?: number;
|
|
1304
|
+
showOnMount?: boolean;
|
|
1305
|
+
}
|
|
1306
|
+
interface GameState {
|
|
1307
|
+
isRunning: boolean;
|
|
1308
|
+
fps: number;
|
|
1309
|
+
deltaTime: number;
|
|
1310
|
+
elapsedTime: number;
|
|
1311
|
+
frameCount: number;
|
|
1312
|
+
mounted: boolean;
|
|
1313
|
+
}
|
|
1314
|
+
interface GameLoopReturn {
|
|
1315
|
+
isRunning: boolean;
|
|
1316
|
+
fps: number;
|
|
1317
|
+
deltaTime: number;
|
|
1318
|
+
elapsedTime: number;
|
|
1319
|
+
frameCount: number;
|
|
1320
|
+
mounted: boolean;
|
|
1321
|
+
start: () => void;
|
|
1322
|
+
stop: () => void;
|
|
1323
|
+
pause: () => void;
|
|
1324
|
+
resume: () => void;
|
|
1325
|
+
reset: () => void;
|
|
1326
|
+
onUpdate: (callback: (deltaTime: number, elapsedTime: number) => void) => void;
|
|
1327
|
+
onRender: (callback: (deltaTime: number, elapsedTime: number) => void) => void;
|
|
1328
|
+
}
|
|
1329
|
+
declare function useGameLoop(options?: GameLoopConfig): GameLoopReturn;
|
|
1330
|
+
|
|
1331
|
+
interface AutoMotionConfig {
|
|
1332
|
+
duration?: number;
|
|
1333
|
+
delay?: number;
|
|
1334
|
+
autoStart?: boolean;
|
|
1335
|
+
easing?: string;
|
|
1336
|
+
type?: "fade" | "slide" | "scale" | "rotate";
|
|
1337
|
+
}
|
|
1338
|
+
interface MotionConfig {
|
|
1339
|
+
duration?: number;
|
|
1340
|
+
delay?: number;
|
|
1341
|
+
autoStart?: boolean;
|
|
1342
|
+
easing?: string;
|
|
1343
|
+
type?: "fade" | "slide" | "scale" | "rotate";
|
|
1344
|
+
}
|
|
1345
|
+
interface MotionFromToConfig {
|
|
1346
|
+
from: Record<string, any>;
|
|
1347
|
+
to: Record<string, any>;
|
|
1348
|
+
duration?: number;
|
|
1349
|
+
delay?: number;
|
|
1350
|
+
autoStart?: boolean;
|
|
1351
|
+
ease?: string;
|
|
1352
|
+
}
|
|
1353
|
+
declare function useMotion(configOrFrom?: MotionConfig | Record<string, any>, to?: Record<string, any>, options?: {
|
|
1354
|
+
duration?: number;
|
|
1355
|
+
delay?: number;
|
|
1356
|
+
autoStart?: boolean;
|
|
1357
|
+
ease?: string;
|
|
1358
|
+
}): {
|
|
1359
|
+
ref: (element: HTMLDivElement | null) => void;
|
|
1360
|
+
style: react__default.CSSProperties;
|
|
1361
|
+
transform: string;
|
|
1362
|
+
opacity: number;
|
|
1363
|
+
backgroundColor: string | undefined;
|
|
1364
|
+
isAnimating: boolean;
|
|
1365
|
+
start: () => void;
|
|
1366
|
+
stop: () => void;
|
|
1367
|
+
reset: () => void;
|
|
1368
|
+
};
|
|
1369
|
+
|
|
1370
|
+
interface ViewportToggleConfig {
|
|
1371
|
+
threshold?: number;
|
|
1372
|
+
rootMargin?: string;
|
|
1373
|
+
trigger?: "enter" | "exit" | "both";
|
|
1374
|
+
once?: boolean;
|
|
1375
|
+
showOnMount?: boolean;
|
|
1376
|
+
}
|
|
1377
|
+
/**
|
|
1378
|
+
* IntersectionObserver를 사용해 뷰포트 진입/이탈을 감지하는 훅.
|
|
1379
|
+
* (구 hua-pro의 useVisibilityToggle — animation lifecycle 기반의
|
|
1380
|
+
* motion-core useVisibilityToggle과 충돌을 피해 이름을 변경함)
|
|
1381
|
+
*/
|
|
1382
|
+
declare function useViewportToggle(options?: ViewportToggleConfig): {
|
|
1383
|
+
ref: react.RefObject<HTMLElement | null>;
|
|
1384
|
+
isVisible: boolean;
|
|
1385
|
+
mounted: boolean;
|
|
1386
|
+
};
|
|
1387
|
+
|
|
1388
|
+
interface ScrollPositionToggleConfig {
|
|
1389
|
+
threshold?: number;
|
|
1390
|
+
showOnMount?: boolean;
|
|
1391
|
+
smooth?: boolean;
|
|
1392
|
+
}
|
|
1393
|
+
/**
|
|
1394
|
+
* window.pageYOffset 기준 스크롤 위치 임계값에 따라 가시성을 토글하는 훅.
|
|
1395
|
+
* (구 hua-pro의 useScrollToggle — animation lifecycle 기반의
|
|
1396
|
+
* motion-core useScrollToggle과 충돌을 피해 이름을 변경함)
|
|
1397
|
+
*/
|
|
1398
|
+
declare function useScrollPositionToggle(options?: ScrollPositionToggleConfig): {
|
|
1399
|
+
isVisible: boolean;
|
|
1400
|
+
scrollToTop: () => void;
|
|
1401
|
+
mounted: boolean;
|
|
1402
|
+
};
|
|
1403
|
+
|
|
1404
|
+
type MotionAs = 'div' | 'span' | 'section' | 'article' | 'header' | 'footer' | 'main' | 'nav';
|
|
1405
|
+
interface MotionProps extends react__default.HTMLAttributes<HTMLElement> {
|
|
1406
|
+
/** HTML 요소 타입 @default 'div' */
|
|
1407
|
+
as?: MotionAs;
|
|
1408
|
+
/** 모션 타입 */
|
|
1409
|
+
type?: EntranceType;
|
|
1410
|
+
/** 멀티이펙트 */
|
|
1411
|
+
effects?: MotionEffects;
|
|
1412
|
+
/** 스크롤 기반 reveal 모드 */
|
|
1413
|
+
scroll?: boolean | ScrollRevealOptions;
|
|
1414
|
+
/** 딜레이 (ms) */
|
|
1415
|
+
delay?: number;
|
|
1416
|
+
/** 지속시간 (ms) */
|
|
1417
|
+
duration?: number;
|
|
1418
|
+
children: react__default.ReactNode;
|
|
1419
|
+
}
|
|
1420
|
+
/**
|
|
1421
|
+
* Motion wrapper component
|
|
1422
|
+
*
|
|
1423
|
+
* ref/style 수동 연결 없이 모션을 적용하는 래퍼 컴포넌트.
|
|
1424
|
+
*
|
|
1425
|
+
* @example
|
|
1426
|
+
* // 기본 fadeIn
|
|
1427
|
+
* <Motion>content</Motion>
|
|
1428
|
+
*
|
|
1429
|
+
* // 스크롤 reveal
|
|
1430
|
+
* <Motion scroll delay={200} className="grid grid-cols-3">...</Motion>
|
|
1431
|
+
*
|
|
1432
|
+
* // 커스텀 요소 + 타입
|
|
1433
|
+
* <Motion as="section" type="slideUp" duration={800}>...</Motion>
|
|
1434
|
+
*/
|
|
1435
|
+
declare function Motion({ as: Component, type, effects, scroll, delay, duration, children, className, style: userStyle, ...rest }: MotionProps): react_jsx_runtime.JSX.Element;
|
|
1436
|
+
|
|
1437
|
+
interface CountUpOptions {
|
|
1438
|
+
/** 목표 숫자 */
|
|
1439
|
+
end: number;
|
|
1440
|
+
/** 숫자 뒤에 붙는 접미사 (%, +, 명 등) */
|
|
1441
|
+
suffix?: string;
|
|
1442
|
+
/** 애니메이션 지속 시간 (ms) */
|
|
1443
|
+
duration?: number;
|
|
1444
|
+
/** 시작 지연 시간 (ms) */
|
|
1445
|
+
delay?: number;
|
|
1446
|
+
/** 애니메이션 활성화 여부 (true가 되면 시작, 한 번만 실행) */
|
|
1447
|
+
active?: boolean;
|
|
1448
|
+
}
|
|
1449
|
+
interface CountUpReturn {
|
|
1450
|
+
/** 현재 숫자 값 */
|
|
1451
|
+
value: number;
|
|
1452
|
+
/** 접미사 포함 표시 문자열 */
|
|
1453
|
+
display: string;
|
|
1454
|
+
}
|
|
1455
|
+
/**
|
|
1456
|
+
* useCountUp — 숫자 카운트업 애니메이션
|
|
1457
|
+
*
|
|
1458
|
+
* 통계, 대시보드, 메트릭 수치 강조에 적합.
|
|
1459
|
+
* ease-out cubic 이징으로 자연스러운 감속 효과.
|
|
1460
|
+
*
|
|
1461
|
+
* @example
|
|
1462
|
+
* ```tsx
|
|
1463
|
+
* const { display } = useCountUp({ end: 1200, suffix: '+', active: isInView })
|
|
1464
|
+
* return <span>{display}</span>
|
|
1465
|
+
* ```
|
|
1466
|
+
*/
|
|
1467
|
+
declare function useCountUp(options: CountUpOptions): CountUpReturn;
|
|
1468
|
+
|
|
1469
|
+
interface ClipRevealOptions {
|
|
1470
|
+
/** 시작 지연 시간 (ms) */
|
|
1471
|
+
delay?: number;
|
|
1472
|
+
/** 애니메이션 지속 시간 (ms) */
|
|
1473
|
+
duration?: number;
|
|
1474
|
+
/** CSS 이징 함수 */
|
|
1475
|
+
easing?: string;
|
|
1476
|
+
/** 애니메이션 활성화 여부 (true가 되면 시작, 한 번만 실행) */
|
|
1477
|
+
active?: boolean;
|
|
1478
|
+
}
|
|
1479
|
+
interface ClipRevealReturn {
|
|
1480
|
+
/** overflow:hidden 컨테이너에 적용할 스타일 */
|
|
1481
|
+
containerStyle: CSSProperties;
|
|
1482
|
+
/** 슬라이딩 텍스트에 적용할 스타일 */
|
|
1483
|
+
textStyle: CSSProperties;
|
|
1484
|
+
/** 등장 완료 여부 */
|
|
1485
|
+
isVisible: boolean;
|
|
1486
|
+
}
|
|
1487
|
+
/**
|
|
1488
|
+
* useClipReveal — overflow clip으로 텍스트가 아래→위로 등장
|
|
1489
|
+
*
|
|
1490
|
+
* 커버 타이틀, 히어로 헤드라인, 큰 문구에 적합.
|
|
1491
|
+
* 컨테이너에 overflow:hidden을 적용하고, 내부 텍스트가 translateY로 올라옴.
|
|
1492
|
+
*
|
|
1493
|
+
* @example
|
|
1494
|
+
* ```tsx
|
|
1495
|
+
* const { containerStyle, textStyle } = useClipReveal({ delay: 200, active: isInView })
|
|
1496
|
+
* return (
|
|
1497
|
+
* <span style={containerStyle}>
|
|
1498
|
+
* <span style={textStyle}>Hello World</span>
|
|
1499
|
+
* </span>
|
|
1500
|
+
* )
|
|
1501
|
+
* ```
|
|
1502
|
+
*/
|
|
1503
|
+
declare function useClipReveal(options?: ClipRevealOptions): ClipRevealReturn;
|
|
1504
|
+
|
|
1505
|
+
interface BlurInOptions {
|
|
1506
|
+
/** 시작 지연 시간 (ms) */
|
|
1507
|
+
delay?: number;
|
|
1508
|
+
/** 애니메이션 지속 시간 (ms) */
|
|
1509
|
+
duration?: number;
|
|
1510
|
+
/** 초기 블러 강도 (px) */
|
|
1511
|
+
blurAmount?: number;
|
|
1512
|
+
/** 초기 스케일 (0~1) */
|
|
1513
|
+
scale?: number;
|
|
1514
|
+
/** CSS 이징 함수 */
|
|
1515
|
+
easing?: string;
|
|
1516
|
+
/** 애니메이션 활성화 여부 (true가 되면 시작, 한 번만 실행) */
|
|
1517
|
+
active?: boolean;
|
|
1518
|
+
}
|
|
1519
|
+
interface BlurInReturn {
|
|
1520
|
+
/** 요소에 적용할 스타일 */
|
|
1521
|
+
style: CSSProperties;
|
|
1522
|
+
/** 등장 완료 여부 */
|
|
1523
|
+
isVisible: boolean;
|
|
1524
|
+
}
|
|
1525
|
+
/**
|
|
1526
|
+
* useBlurIn — 블러에서 선명하게 + 스케일 등장
|
|
1527
|
+
*
|
|
1528
|
+
* 인용구, 임팩트 문구, 핵심 메시지에 적합.
|
|
1529
|
+
* blur + scale + opacity 세 가지가 동시에 전환됨.
|
|
1530
|
+
*
|
|
1531
|
+
* @example
|
|
1532
|
+
* ```tsx
|
|
1533
|
+
* const { style } = useBlurIn({ blurAmount: 12, scale: 0.95, active: isInView })
|
|
1534
|
+
* return <blockquote style={style}>Deep insight here</blockquote>
|
|
1535
|
+
* ```
|
|
1536
|
+
*/
|
|
1537
|
+
declare function useBlurIn(options?: BlurInOptions): BlurInReturn;
|
|
1538
|
+
|
|
1539
|
+
interface UseStaggerOptions {
|
|
1540
|
+
/** 자식 아이템 개수 */
|
|
1541
|
+
count: number;
|
|
1542
|
+
/** 아이템 간 딜레이 (ms) @default 100 */
|
|
1543
|
+
staggerDelay?: number;
|
|
1544
|
+
/** 전체 시작 딜레이 (ms) @default 0 */
|
|
1545
|
+
baseDelay?: number;
|
|
1546
|
+
/** 애니메이션 지속시간 (ms) @default 700 */
|
|
1547
|
+
duration?: number;
|
|
1548
|
+
/** 모션 타입 @default 'fadeIn' */
|
|
1549
|
+
motionType?: ScrollRevealMotionType;
|
|
1550
|
+
/** IntersectionObserver 임계값 @default 0.1 */
|
|
1551
|
+
threshold?: number;
|
|
1552
|
+
/** 이징 함수 @default 'ease-out' */
|
|
1553
|
+
easing?: string;
|
|
1554
|
+
}
|
|
1555
|
+
interface UseStaggerReturn {
|
|
1556
|
+
/** 부모 컨테이너 ref — IntersectionObserver 연결 */
|
|
1557
|
+
containerRef: RefObject<HTMLDivElement | null>;
|
|
1558
|
+
/** 자식 아이템별 style 배열 */
|
|
1559
|
+
styles: CSSProperties[];
|
|
1560
|
+
/** 전체 visible 여부 */
|
|
1561
|
+
isVisible: boolean;
|
|
1562
|
+
}
|
|
1563
|
+
/**
|
|
1564
|
+
* useStagger — 리스트 아이템에 순차 딜레이를 적용하는 훅
|
|
1565
|
+
*
|
|
1566
|
+
* 단일 IntersectionObserver로 컨테이너를 관찰하고,
|
|
1567
|
+
* 자식 아이템별로 CSS transition-delay를 자동 계산합니다.
|
|
1568
|
+
*
|
|
1569
|
+
* @example
|
|
1570
|
+
* const stagger = useStagger({ count: items.length, staggerDelay: 100 });
|
|
1571
|
+
* <div ref={stagger.containerRef}>
|
|
1572
|
+
* {items.map((item, i) => (
|
|
1573
|
+
* <div style={stagger.styles[i]} key={i}>{item}</div>
|
|
1574
|
+
* ))}
|
|
1575
|
+
* </div>
|
|
1576
|
+
*/
|
|
1577
|
+
declare function useStagger(options: UseStaggerOptions): UseStaggerReturn;
|
|
1578
|
+
|
|
1579
|
+
export { type AutoFadeConfig, type AutoMotionConfig, type AutoPlayConfig, type AutoScaleConfig, type AutoSlideConfig, BaseMotionOptions, BaseMotionReturn, type BlurInOptions, type BlurInReturn, BounceOptions, type ButtonEffectOptions, type CardListOptions, type ClipRevealOptions, type ClipRevealReturn, type CountUpOptions, type CountUpReturn, type CustomCursorOptions, type CustomCursorReturn, type ElementProgressOptions, type ElementProgressReturn, EntranceMotionReturn, EntranceType, FadeInOptions, type GameLoopConfig, type GameState, GradientOptions, HoverMotionOptions, InViewOptions, InViewReturn, InteractionReturn, type InteractiveConfig, type InteractiveState, type KeyboardToggleConfig, type LanguageConfig, type LayoutMotionConfig, type LayoutState, type LoadingSpinnerOptions, type MagneticCursorOptions, type MagneticCursorReturn, Motion, type MotionEffects, MotionElement, type MotionFromToConfig, type MotionProps, MouseOptions, MouseReturn, type NavigationOptions, PageMotionRef, PageMotionsConfig, PageType, type PerformanceMonitorConfig, type PerformanceMonitorState, PulseOptions, ReducedMotionReturn, RepeatOptions, ScaleOptions, type ScrollDirectionConfig, type ScrollPositionToggleConfig, ScrollRevealMotionType, ScrollRevealOptions, type ScrollToggleOptions, type SequenceConfig, type SkeletonOptions, SlideOptions, type SmoothScrollOptions, type SmoothScrollReturn, SpringOptions, type StickyToggleConfig, ToggleMotionOptions, type TypewriterOptions, type TypewriterReturn, type MotionConfig as UseMotionConfig, type OrchestrationConfig as UseOrchestrationConfig, type UseStaggerOptions, type UseStaggerReturn, type UseUnifiedMotionOptions, type ViewportToggleConfig, type VisibilityToggleOptions, WindowSizeOptions, WindowSizeReturn, createLayoutTransition, observeElement, useAutoFade, useAutoPlay, useAutoScale, useAutoSlide, useBlurIn, useBounceIn, useButtonEffect, useCardList, useClickToggle, useClipReveal, useCountUp, useCustomCursor, useElementProgress, useFadeIn, useFocusToggle, useGameLoop, useGesture, useGestureMotion, useGradient, useHoverMotion, useInView, useInteractive, useKeyboardToggle, useLanguageAwareMotion, useLayoutMotion, useLoadingSpinner, useMagneticCursor, useMotion, useMotionOrchestra, useMotionState, useMouse, useNavigation, useOrchestration, usePageMotions, usePerformanceMonitor, usePulse, useReducedMotion, useReducedMotionObject, useRepeat, useScaleIn, useScrollDirection, useScrollPositionToggle, useScrollProgress, useScrollReveal, useScrollToggle, useSequence, useSimplePageMotion, useSkeleton, useSlideDown, useSlideLeft, useSlideRight, useSlideUp, useSmartMotion, useSmoothScroll, useSpringMotion, useStagger, useStickyToggle, useToggleMotion, useTypewriter, useUnifiedMotion, useViewportToggle, useVisibilityToggle, useWindowSize };
|