react-ui-animate 5.2.0 → 5.3.0-next.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/README.md +651 -114
- package/dist/index.d.ts +651 -5
- package/dist/index.mjs +1 -0
- package/package.json +34 -24
- package/dist/animation/Config.d.ts +0 -63
- package/dist/animation/descriptors.d.ts +0 -7
- package/dist/animation/drivers.d.ts +0 -4
- package/dist/animation/helpers.d.ts +0 -3
- package/dist/animation/hooks/index.d.ts +0 -2
- package/dist/animation/hooks/useMount.d.ts +0 -16
- package/dist/animation/hooks/useValue.d.ts +0 -9
- package/dist/animation/index.d.ts +0 -5
- package/dist/animation/modules/Mount.d.ts +0 -17
- package/dist/animation/modules/index.d.ts +0 -1
- package/dist/animation/to.d.ts +0 -2
- package/dist/animation/types.d.ts +0 -42
- package/dist/gestures/controllers/DragGesture.d.ts +0 -47
- package/dist/gestures/controllers/Gesture.d.ts +0 -13
- package/dist/gestures/controllers/MoveGesture.d.ts +0 -30
- package/dist/gestures/controllers/ScrollGesture.d.ts +0 -29
- package/dist/gestures/controllers/WheelGesture.d.ts +0 -28
- package/dist/gestures/hooks/index.d.ts +0 -4
- package/dist/gestures/hooks/useDrag.d.ts +0 -5
- package/dist/gestures/hooks/useMove.d.ts +0 -8
- package/dist/gestures/hooks/useRecognizer.d.ts +0 -13
- package/dist/gestures/hooks/useScroll.d.ts +0 -11
- package/dist/gestures/hooks/useWheel.d.ts +0 -8
- package/dist/hooks/events/useOutsideClick.d.ts +0 -2
- package/dist/hooks/index.d.ts +0 -2
- package/dist/hooks/observers/useInView.d.ts +0 -5
- package/dist/hooks/observers/useScrollProgress.d.ts +0 -23
- package/dist/index.js +0 -2
- package/dist/index.js.map +0 -1
- package/dist/utils/index.d.ts +0 -4
package/README.md
CHANGED
|
@@ -4,9 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
> Create smooth animations and interactive gestures in React applications effortlessly.
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
You can install `react-ui-animate` via `npm` or `yarn`:
|
|
7
|
+
## Installation
|
|
10
8
|
|
|
11
9
|
```sh
|
|
12
10
|
npm install react-ui-animate
|
|
@@ -18,49 +16,157 @@ yarn add react-ui-animate
|
|
|
18
16
|
|
|
19
17
|
---
|
|
20
18
|
|
|
21
|
-
##
|
|
19
|
+
## Quick Start
|
|
20
|
+
|
|
21
|
+
The `react-ui-animate` library provides a declarative way to add animations and gestures to your React components. Here's a simple example:
|
|
22
|
+
|
|
23
|
+
```tsx
|
|
24
|
+
import { animate, withSpring } from 'react-ui-animate';
|
|
25
|
+
|
|
26
|
+
function App() {
|
|
27
|
+
return (
|
|
28
|
+
<animate.div
|
|
29
|
+
style={{
|
|
30
|
+
width: 100,
|
|
31
|
+
height: 100,
|
|
32
|
+
backgroundColor: 'blue',
|
|
33
|
+
scale: 0.5,
|
|
34
|
+
opacity: 0,
|
|
35
|
+
}}
|
|
36
|
+
animate={{
|
|
37
|
+
scale: withSpring(1),
|
|
38
|
+
opacity: withSpring(1),
|
|
39
|
+
}}
|
|
40
|
+
/>
|
|
41
|
+
);
|
|
42
|
+
}
|
|
43
|
+
```
|
|
22
44
|
|
|
23
|
-
|
|
45
|
+
---
|
|
24
46
|
|
|
25
|
-
|
|
47
|
+
## Core Concepts
|
|
26
48
|
|
|
27
|
-
|
|
49
|
+
### 1. Animate Component
|
|
50
|
+
|
|
51
|
+
The `animate` object provides animated versions of all HTML elements. Use `animate.div`, `animate.button`, `animate.span`, etc., just like regular React elements.
|
|
52
|
+
|
|
53
|
+
#### Basic Animation
|
|
54
|
+
|
|
55
|
+
Use the `animate` prop to define animations that run when the component mounts or when the prop changes:
|
|
28
56
|
|
|
29
57
|
```tsx
|
|
30
|
-
import
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
58
|
+
import { animate, withSpring, withTiming } from 'react-ui-animate';
|
|
59
|
+
|
|
60
|
+
<animate.div
|
|
61
|
+
style={{
|
|
62
|
+
width: 100,
|
|
63
|
+
height: 100,
|
|
64
|
+
backgroundColor: 'red',
|
|
65
|
+
translateX: 0,
|
|
66
|
+
}}
|
|
67
|
+
animate={{
|
|
68
|
+
translateX: withSpring(200),
|
|
69
|
+
backgroundColor: withTiming('blue', { duration: 500 }),
|
|
70
|
+
}}
|
|
71
|
+
/>
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
#### Exit Animations with Presence
|
|
38
75
|
|
|
39
|
-
|
|
76
|
+
Wrap components in `Presence` to enable exit animations when they're removed:
|
|
77
|
+
|
|
78
|
+
```tsx
|
|
79
|
+
import { Presence, animate, withSpring, withTiming } from 'react-ui-animate';
|
|
80
|
+
|
|
81
|
+
function Modal({ isOpen, onClose }) {
|
|
82
|
+
return (
|
|
83
|
+
<Presence>
|
|
84
|
+
{isOpen && (
|
|
85
|
+
<animate.div
|
|
86
|
+
key="modal"
|
|
87
|
+
style={{
|
|
88
|
+
opacity: 0,
|
|
89
|
+
scale: 0.8,
|
|
90
|
+
}}
|
|
91
|
+
animate={{
|
|
92
|
+
opacity: withTiming(1),
|
|
93
|
+
scale: withSpring(1),
|
|
94
|
+
}}
|
|
95
|
+
exit={{
|
|
96
|
+
opacity: withTiming(0),
|
|
97
|
+
scale: withSpring(0.8),
|
|
98
|
+
}}
|
|
99
|
+
>
|
|
100
|
+
Modal Content
|
|
101
|
+
</animate.div>
|
|
102
|
+
)}
|
|
103
|
+
</Presence>
|
|
104
|
+
);
|
|
105
|
+
}
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
#### State-Based Animations
|
|
109
|
+
|
|
110
|
+
React to user interactions with `hover`, `press`, `focus`, and `view` props:
|
|
111
|
+
|
|
112
|
+
```tsx
|
|
113
|
+
<animate.button
|
|
114
|
+
style={{
|
|
115
|
+
scale: 1,
|
|
116
|
+
backgroundColor: '#3399ff',
|
|
117
|
+
}}
|
|
118
|
+
hover={{
|
|
119
|
+
scale: withSpring(1.05),
|
|
120
|
+
backgroundColor: withTiming('#4da6ff'),
|
|
121
|
+
}}
|
|
122
|
+
press={{
|
|
123
|
+
scale: withSpring(0.95),
|
|
124
|
+
}}
|
|
125
|
+
>
|
|
126
|
+
Hover Me
|
|
127
|
+
</animate.button>
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
**View Animations** - Animate when elements enter the viewport:
|
|
131
|
+
|
|
132
|
+
```tsx
|
|
133
|
+
<animate.div
|
|
134
|
+
style={{
|
|
135
|
+
opacity: 0,
|
|
136
|
+
translateY: 50,
|
|
137
|
+
}}
|
|
138
|
+
view={{
|
|
139
|
+
opacity: withTiming(1),
|
|
140
|
+
translateY: withSpring(0),
|
|
141
|
+
}}
|
|
142
|
+
viewOptions={{ threshold: 0.3, once: true }}
|
|
143
|
+
>
|
|
144
|
+
This animates when scrolled into view
|
|
145
|
+
</animate.div>
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
### 2. useValue Hook
|
|
149
|
+
|
|
150
|
+
Create and control animated values programmatically:
|
|
151
|
+
|
|
152
|
+
```tsx
|
|
153
|
+
import { useValue, animate, withSpring, withSequence, withTiming } from 'react-ui-animate';
|
|
154
|
+
|
|
155
|
+
function AnimatedBox() {
|
|
40
156
|
const [width, setWidth] = useValue(100);
|
|
157
|
+
const [x, setX] = useValue(0);
|
|
41
158
|
|
|
42
159
|
return (
|
|
43
160
|
<>
|
|
44
|
-
<button
|
|
45
|
-
|
|
46
|
-
setWidth(withSequence([withTiming(100), withSpring(0)]));
|
|
47
|
-
}}
|
|
48
|
-
>
|
|
49
|
-
SEQUENCE (100 → 0)
|
|
50
|
-
</button>
|
|
51
|
-
<button
|
|
52
|
-
onClick={() => {
|
|
53
|
-
setWidth(withSpring(200));
|
|
54
|
-
}}
|
|
55
|
-
>
|
|
56
|
-
SPRING (→ 200)
|
|
161
|
+
<button onClick={() => setWidth(withSpring(200))}>
|
|
162
|
+
Expand
|
|
57
163
|
</button>
|
|
58
|
-
<button
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
}
|
|
62
|
-
>
|
|
63
|
-
|
|
164
|
+
<button onClick={() => setX(withSequence([
|
|
165
|
+
withTiming(100, { duration: 300 }),
|
|
166
|
+
withTiming(200, { duration: 300 }),
|
|
167
|
+
withTiming(0, { duration: 300 }),
|
|
168
|
+
]))}>
|
|
169
|
+
Sequence
|
|
64
170
|
</button>
|
|
65
171
|
|
|
66
172
|
<animate.div
|
|
@@ -68,119 +174,235 @@ export const UseValue: React.FC = () => {
|
|
|
68
174
|
width,
|
|
69
175
|
height: 100,
|
|
70
176
|
backgroundColor: 'red',
|
|
71
|
-
|
|
72
|
-
top: 0,
|
|
177
|
+
translateX: x,
|
|
73
178
|
}}
|
|
74
179
|
/>
|
|
75
180
|
</>
|
|
76
181
|
);
|
|
77
|
-
}
|
|
182
|
+
}
|
|
78
183
|
```
|
|
79
184
|
|
|
80
|
-
|
|
185
|
+
**Interpolation** - Map values to different ranges:
|
|
81
186
|
|
|
82
|
-
|
|
187
|
+
```tsx
|
|
188
|
+
const [progress, setProgress] = useValue(0);
|
|
189
|
+
|
|
190
|
+
<animate.div
|
|
191
|
+
style={{
|
|
192
|
+
backgroundColor: progress.to([0, 1], ['red', 'blue']),
|
|
193
|
+
translateX: progress.to([0, 1], [0, 500]),
|
|
194
|
+
}}
|
|
195
|
+
/>
|
|
196
|
+
```
|
|
197
|
+
|
|
198
|
+
**Controls** - Control animations programmatically:
|
|
199
|
+
|
|
200
|
+
```tsx
|
|
201
|
+
const [value, setValue, controls] = useValue(0);
|
|
202
|
+
|
|
203
|
+
// Start animation
|
|
204
|
+
setValue(withSpring(100));
|
|
205
|
+
|
|
206
|
+
// Control playback
|
|
207
|
+
controls.pause();
|
|
208
|
+
controls.resume();
|
|
209
|
+
controls.cancel();
|
|
210
|
+
controls.reset();
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
### 3. Animation Descriptors
|
|
214
|
+
|
|
215
|
+
Animation descriptors define how animations behave. They can be used with both `animate` components and `useValue`.
|
|
216
|
+
|
|
217
|
+
#### withSpring
|
|
218
|
+
|
|
219
|
+
Creates physics-based spring animations:
|
|
220
|
+
|
|
221
|
+
```tsx
|
|
222
|
+
withSpring(100, {
|
|
223
|
+
stiffness: 100, // Spring stiffness (default: 100)
|
|
224
|
+
damping: 15, // Spring damping (default: 15)
|
|
225
|
+
mass: 1, // Spring mass (default: 1)
|
|
226
|
+
})
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
#### withTiming
|
|
230
|
+
|
|
231
|
+
Creates time-based animations with easing:
|
|
232
|
+
|
|
233
|
+
```tsx
|
|
234
|
+
withTiming(100, {
|
|
235
|
+
duration: 500, // Duration in milliseconds
|
|
236
|
+
easing: Easing.easeInOut, // Easing function
|
|
237
|
+
})
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
#### withDecay
|
|
241
|
+
|
|
242
|
+
Creates momentum-based decay animations:
|
|
243
|
+
|
|
244
|
+
```tsx
|
|
245
|
+
withDecay({
|
|
246
|
+
velocity: 100, // Initial velocity
|
|
247
|
+
clamp: [0, 500], // Optional: clamp values
|
|
248
|
+
})
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
#### withSequence
|
|
252
|
+
|
|
253
|
+
Runs animations one after another:
|
|
254
|
+
|
|
255
|
+
```tsx
|
|
256
|
+
withSequence([
|
|
257
|
+
withTiming(100, { duration: 300 }),
|
|
258
|
+
withTiming(200, { duration: 300 }),
|
|
259
|
+
withSpring(0, { stiffness: 200 }),
|
|
260
|
+
])
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
#### withLoop
|
|
264
|
+
|
|
265
|
+
Repeats animations:
|
|
266
|
+
|
|
267
|
+
```tsx
|
|
268
|
+
withLoop(
|
|
269
|
+
withSequence([
|
|
270
|
+
withTiming(90, { duration: 500 }),
|
|
271
|
+
withTiming(180, { duration: 500 }),
|
|
272
|
+
withTiming(360, { duration: 500 }),
|
|
273
|
+
]),
|
|
274
|
+
3 // Number of iterations (0 = infinite)
|
|
275
|
+
)
|
|
276
|
+
```
|
|
277
|
+
|
|
278
|
+
#### withDelay
|
|
279
|
+
|
|
280
|
+
Adds delay to animations (typically used inside `withSequence`):
|
|
281
|
+
|
|
282
|
+
```tsx
|
|
283
|
+
withSequence([
|
|
284
|
+
withDelay(500),
|
|
285
|
+
withTiming(1, { duration: 500 }),
|
|
286
|
+
])
|
|
287
|
+
```
|
|
288
|
+
|
|
289
|
+
### 4. Animation Recipes
|
|
290
|
+
|
|
291
|
+
Pre-built animation recipes for common use cases:
|
|
83
292
|
|
|
84
293
|
```tsx
|
|
85
|
-
import React from 'react';
|
|
86
294
|
import {
|
|
87
295
|
animate,
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
296
|
+
fadeIn,
|
|
297
|
+
slideInUp,
|
|
298
|
+
scaleIn,
|
|
299
|
+
bounceIn,
|
|
300
|
+
hoverScale,
|
|
301
|
+
pressScale,
|
|
93
302
|
} from 'react-ui-animate';
|
|
94
303
|
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
304
|
+
// Enter animations
|
|
305
|
+
<animate.div animate={fadeIn}>Fades in</animate.div>
|
|
306
|
+
<animate.div animate={slideInUp}>Slides up</animate.div>
|
|
307
|
+
<animate.div animate={scaleIn}>Scales in</animate.div>
|
|
308
|
+
<animate.div animate={bounceIn}>Bounces in</animate.div>
|
|
98
309
|
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
isMounted && (
|
|
104
|
-
<animate.div
|
|
105
|
-
style={{
|
|
106
|
-
width: 100,
|
|
107
|
-
height: 100,
|
|
108
|
-
backgroundColor: 'teal',
|
|
109
|
-
opacity: animation,
|
|
110
|
-
}}
|
|
111
|
-
/>
|
|
112
|
-
)
|
|
113
|
-
)}
|
|
310
|
+
// State animations
|
|
311
|
+
<animate.button hover={hoverScale} press={pressScale}>
|
|
312
|
+
Interactive Button
|
|
313
|
+
</animate.button>
|
|
114
314
|
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
);
|
|
118
|
-
};
|
|
315
|
+
// Exit animations
|
|
316
|
+
<animate.div exit={exitFade}>Fades out</animate.div>
|
|
119
317
|
```
|
|
120
318
|
|
|
121
|
-
|
|
319
|
+
**Available Recipes:**
|
|
122
320
|
|
|
123
|
-
|
|
321
|
+
- **Fade**: `fadeIn`, `fadeOut`, `fadeInUp`, `fadeInDown`, `fadeInLeft`, `fadeInRight`
|
|
322
|
+
- **Slide**: `slideInUp`, `slideInDown`, `slideInLeft`, `slideInRight`, `slideOutUp`, `slideOutDown`, `slideOutLeft`, `slideOutRight`
|
|
323
|
+
- **Scale**: `scaleIn`, `scaleOut`, `scaleUp`, `scaleDown`
|
|
324
|
+
- **Bounce**: `bounceIn`, `bounceOut`
|
|
325
|
+
- **Rotate**: `rotateIn`, `rotateOut`, `spin`
|
|
326
|
+
- **Zoom**: `zoomIn`, `zoomOut`
|
|
327
|
+
- **Flip**: `flipX`, `flipY`
|
|
328
|
+
- **Combined**: `slideFadeIn`, `slideFadeOut`, `scaleFadeIn`, `scaleFadeOut`
|
|
329
|
+
- **Hover**: `hoverScale`, `hoverLift`, `hoverGlow`
|
|
330
|
+
- **Press**: `pressScale`, `pressDown`
|
|
331
|
+
- **Exit**: `exitFade`, `exitSlideUp`, `exitSlideDown`, `exitScale`
|
|
124
332
|
|
|
125
|
-
|
|
126
|
-
import React, { useLayoutEffect, useState } from 'react';
|
|
127
|
-
import { animate, useValue, withSpring } from 'react-ui-animate';
|
|
333
|
+
### 5. Presence Component
|
|
128
334
|
|
|
129
|
-
|
|
130
|
-
const [open, setOpen] = useState(false);
|
|
131
|
-
const [x, setX] = useValue(0);
|
|
335
|
+
`Presence` manages mount and unmount animations for components:
|
|
132
336
|
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
}, [open, setX]);
|
|
337
|
+
```tsx
|
|
338
|
+
import { Presence, animate, withSpring } from 'react-ui-animate';
|
|
136
339
|
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
<animate.div
|
|
140
|
-
style={{
|
|
141
|
-
width: 100,
|
|
142
|
-
height: 100,
|
|
143
|
-
backgroundColor: x.to([0, 500], ['red', 'blue']),
|
|
144
|
-
translateX: x,
|
|
145
|
-
}}
|
|
146
|
-
/>
|
|
340
|
+
function List() {
|
|
341
|
+
const [items, setItems] = useState(['Item 1', 'Item 2']);
|
|
147
342
|
|
|
148
|
-
|
|
149
|
-
|
|
343
|
+
return (
|
|
344
|
+
<Presence mode="sync" onExitComplete={() => console.log('All exited')}>
|
|
345
|
+
{items.map((item) => (
|
|
346
|
+
<animate.div
|
|
347
|
+
key={item}
|
|
348
|
+
animate={{ opacity: withSpring(1) }}
|
|
349
|
+
exit={{ opacity: withSpring(0) }}
|
|
350
|
+
>
|
|
351
|
+
{item}
|
|
352
|
+
</animate.div>
|
|
353
|
+
))}
|
|
354
|
+
</Presence>
|
|
150
355
|
);
|
|
151
|
-
}
|
|
356
|
+
}
|
|
152
357
|
```
|
|
153
358
|
|
|
154
|
-
|
|
359
|
+
**Presence Props:**
|
|
155
360
|
|
|
156
|
-
|
|
361
|
+
- `mode`: `'sync'` (default) | `'wait'` | `'popLayout'`
|
|
362
|
+
- `sync`: Exiting and entering animate simultaneously
|
|
363
|
+
- `wait`: Exiting complete before entering start
|
|
364
|
+
- `popLayout`: Exiting removed from layout immediately
|
|
365
|
+
- `initial`: Skip enter animation on initial mount (default: `true`)
|
|
366
|
+
- `onExitComplete`: Callback when all exits complete
|
|
157
367
|
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
368
|
+
**Presence Hooks:**
|
|
369
|
+
|
|
370
|
+
```tsx
|
|
371
|
+
import { usePresence, useIsPresent } from 'react-ui-animate';
|
|
372
|
+
|
|
373
|
+
function AnimatedItem() {
|
|
374
|
+
const [isPresent, onExitComplete] = usePresence();
|
|
375
|
+
const isPresent2 = useIsPresent(); // Simple boolean check
|
|
376
|
+
|
|
377
|
+
// Use isPresent to conditionally render or animate
|
|
378
|
+
return isPresent ? <div>Content</div> : null;
|
|
379
|
+
}
|
|
380
|
+
```
|
|
162
381
|
|
|
163
|
-
|
|
382
|
+
### 6. Gestures
|
|
164
383
|
|
|
165
|
-
|
|
384
|
+
React to user gestures with built-in hooks:
|
|
166
385
|
|
|
167
|
-
|
|
168
|
-
- `useMove`
|
|
169
|
-
- `useScroll`
|
|
170
|
-
- `useWheel`
|
|
386
|
+
#### useDrag
|
|
171
387
|
|
|
172
|
-
|
|
388
|
+
Handle drag gestures:
|
|
173
389
|
|
|
174
390
|
```tsx
|
|
175
|
-
import React from 'react';
|
|
176
391
|
import { useValue, animate, useDrag, withSpring } from 'react-ui-animate';
|
|
177
392
|
|
|
178
|
-
|
|
393
|
+
function Draggable() {
|
|
179
394
|
const ref = useRef(null);
|
|
180
|
-
const [
|
|
395
|
+
const [x, setX] = useValue(0);
|
|
396
|
+
const [y, setY] = useValue(0);
|
|
181
397
|
|
|
182
398
|
useDrag(ref, ({ down, movement }) => {
|
|
183
|
-
|
|
399
|
+
if (down) {
|
|
400
|
+
setX(movement.x);
|
|
401
|
+
setY(movement.y);
|
|
402
|
+
} else {
|
|
403
|
+
setX(withSpring(0));
|
|
404
|
+
setY(withSpring(0));
|
|
405
|
+
}
|
|
184
406
|
});
|
|
185
407
|
|
|
186
408
|
return (
|
|
@@ -189,18 +411,333 @@ export const Draggable: React.FC = () => {
|
|
|
189
411
|
style={{
|
|
190
412
|
width: 100,
|
|
191
413
|
height: 100,
|
|
192
|
-
backgroundColor: '
|
|
193
|
-
translateX,
|
|
414
|
+
backgroundColor: 'blue',
|
|
415
|
+
translateX: x,
|
|
416
|
+
translateY: y,
|
|
194
417
|
}}
|
|
195
418
|
/>
|
|
196
419
|
);
|
|
197
|
-
}
|
|
420
|
+
}
|
|
421
|
+
```
|
|
422
|
+
|
|
423
|
+
#### useMove
|
|
424
|
+
|
|
425
|
+
Track pointer movement:
|
|
426
|
+
|
|
427
|
+
```tsx
|
|
428
|
+
import { useMove } from 'react-ui-animate';
|
|
429
|
+
|
|
430
|
+
useMove(ref, ({ movement }) => {
|
|
431
|
+
console.log('Moving:', movement.x, movement.y);
|
|
432
|
+
});
|
|
433
|
+
```
|
|
434
|
+
|
|
435
|
+
#### useScroll
|
|
436
|
+
|
|
437
|
+
React to scroll events:
|
|
438
|
+
|
|
439
|
+
```tsx
|
|
440
|
+
import { useScroll } from 'react-ui-animate';
|
|
441
|
+
|
|
442
|
+
useScroll(ref, ({ scroll }) => {
|
|
443
|
+
console.log('Scrolled:', scroll.x, scroll.y);
|
|
444
|
+
});
|
|
445
|
+
```
|
|
446
|
+
|
|
447
|
+
#### useWheel
|
|
448
|
+
|
|
449
|
+
Handle wheel events:
|
|
450
|
+
|
|
451
|
+
```tsx
|
|
452
|
+
import { useWheel } from 'react-ui-animate';
|
|
453
|
+
|
|
454
|
+
useWheel(ref, ({ delta }) => {
|
|
455
|
+
console.log('Wheel delta:', delta.x, delta.y);
|
|
456
|
+
});
|
|
457
|
+
```
|
|
458
|
+
|
|
459
|
+
#### useScrollProgress
|
|
460
|
+
|
|
461
|
+
Track scroll progress:
|
|
462
|
+
|
|
463
|
+
```tsx
|
|
464
|
+
import { useScrollProgress } from 'react-ui-animate';
|
|
465
|
+
|
|
466
|
+
const progress = useScrollProgress(ref, {
|
|
467
|
+
start: 0, // Start tracking at 0% scroll
|
|
468
|
+
end: 100, // End tracking at 100% scroll
|
|
469
|
+
axis: 'y', // 'x' or 'y'
|
|
470
|
+
});
|
|
471
|
+
```
|
|
472
|
+
|
|
473
|
+
### 7. Utilities
|
|
474
|
+
|
|
475
|
+
#### makeAnimated
|
|
476
|
+
|
|
477
|
+
Create custom animated components:
|
|
478
|
+
|
|
479
|
+
```tsx
|
|
480
|
+
import { makeAnimated } from 'react-ui-animate';
|
|
481
|
+
|
|
482
|
+
const AnimatedButton = makeAnimated('button');
|
|
483
|
+
const AnimatedSection = makeAnimated('section');
|
|
484
|
+
|
|
485
|
+
<AnimatedButton animate={{ scale: withSpring(1.1) }}>
|
|
486
|
+
Custom Button
|
|
487
|
+
</AnimatedButton>
|
|
488
|
+
```
|
|
489
|
+
|
|
490
|
+
#### to (Interpolation)
|
|
491
|
+
|
|
492
|
+
Map values between ranges:
|
|
493
|
+
|
|
494
|
+
```tsx
|
|
495
|
+
import { to } from 'react-ui-animate';
|
|
496
|
+
|
|
497
|
+
const interpolate = to([0, 100], [0, 500]);
|
|
498
|
+
interpolate(50); // Returns 250
|
|
499
|
+
|
|
500
|
+
// Color interpolation
|
|
501
|
+
const colorInterpolate = to([0, 1], ['red', 'blue']);
|
|
502
|
+
colorInterpolate(0.5); // Returns interpolated color
|
|
503
|
+
```
|
|
504
|
+
|
|
505
|
+
#### combine
|
|
506
|
+
|
|
507
|
+
Combine multiple animated values:
|
|
508
|
+
|
|
509
|
+
```tsx
|
|
510
|
+
import { combine, useValue } from 'react-ui-animate';
|
|
511
|
+
|
|
512
|
+
const [x, setX] = useValue(0);
|
|
513
|
+
const [y, setY] = useValue(0);
|
|
514
|
+
const combined = combine(x, y, (x, y) => x + y);
|
|
515
|
+
```
|
|
516
|
+
|
|
517
|
+
### 8. Additional Hooks
|
|
518
|
+
|
|
519
|
+
#### useInView
|
|
520
|
+
|
|
521
|
+
Detect when elements enter the viewport:
|
|
522
|
+
|
|
523
|
+
```tsx
|
|
524
|
+
import { useInView } from 'react-ui-animate';
|
|
525
|
+
|
|
526
|
+
const ref = useRef(null);
|
|
527
|
+
const isInView = useInView(ref, {
|
|
528
|
+
threshold: 0.5,
|
|
529
|
+
once: true,
|
|
530
|
+
});
|
|
531
|
+
```
|
|
532
|
+
|
|
533
|
+
#### useOutsideClick
|
|
534
|
+
|
|
535
|
+
Detect clicks outside an element:
|
|
536
|
+
|
|
537
|
+
```tsx
|
|
538
|
+
import { useOutsideClick } from 'react-ui-animate';
|
|
539
|
+
|
|
540
|
+
const ref = useRef(null);
|
|
541
|
+
useOutsideClick(ref, () => {
|
|
542
|
+
console.log('Clicked outside!');
|
|
543
|
+
});
|
|
544
|
+
```
|
|
545
|
+
|
|
546
|
+
---
|
|
547
|
+
|
|
548
|
+
## Complete Examples
|
|
549
|
+
|
|
550
|
+
### Modal with Exit Animation
|
|
551
|
+
|
|
552
|
+
```tsx
|
|
553
|
+
import { useState, useRef } from 'react';
|
|
554
|
+
import {
|
|
555
|
+
Presence,
|
|
556
|
+
animate,
|
|
557
|
+
useOutsideClick,
|
|
558
|
+
withSpring,
|
|
559
|
+
withTiming,
|
|
560
|
+
} from 'react-ui-animate';
|
|
561
|
+
|
|
562
|
+
function Modal({ isOpen, onClose }) {
|
|
563
|
+
const ref = useRef(null);
|
|
564
|
+
useOutsideClick(ref, onClose);
|
|
565
|
+
|
|
566
|
+
return (
|
|
567
|
+
<Presence>
|
|
568
|
+
{isOpen && (
|
|
569
|
+
<>
|
|
570
|
+
{/* Backdrop */}
|
|
571
|
+
<animate.div
|
|
572
|
+
key="backdrop"
|
|
573
|
+
style={{
|
|
574
|
+
position: 'fixed',
|
|
575
|
+
inset: 0,
|
|
576
|
+
backgroundColor: 'rgba(0,0,0,0)',
|
|
577
|
+
opacity: 0,
|
|
578
|
+
}}
|
|
579
|
+
animate={{
|
|
580
|
+
backgroundColor: withTiming('rgba(0,0,0,0.5)'),
|
|
581
|
+
opacity: withTiming(1),
|
|
582
|
+
}}
|
|
583
|
+
exit={{
|
|
584
|
+
backgroundColor: withTiming('rgba(0,0,0,0)'),
|
|
585
|
+
opacity: withTiming(0),
|
|
586
|
+
}}
|
|
587
|
+
/>
|
|
588
|
+
|
|
589
|
+
{/* Modal */}
|
|
590
|
+
<animate.div
|
|
591
|
+
key="modal"
|
|
592
|
+
ref={ref}
|
|
593
|
+
style={{
|
|
594
|
+
position: 'fixed',
|
|
595
|
+
inset: 0,
|
|
596
|
+
display: 'flex',
|
|
597
|
+
alignItems: 'center',
|
|
598
|
+
justifyContent: 'center',
|
|
599
|
+
scale: 0.8,
|
|
600
|
+
opacity: 0,
|
|
601
|
+
}}
|
|
602
|
+
animate={{
|
|
603
|
+
scale: withSpring(1),
|
|
604
|
+
opacity: withSpring(1),
|
|
605
|
+
}}
|
|
606
|
+
exit={{
|
|
607
|
+
scale: withSpring(0.8),
|
|
608
|
+
opacity: withSpring(0),
|
|
609
|
+
}}
|
|
610
|
+
>
|
|
611
|
+
<div style={{ backgroundColor: 'white', padding: 20 }}>
|
|
612
|
+
Modal Content
|
|
613
|
+
</div>
|
|
614
|
+
</animate.div>
|
|
615
|
+
</>
|
|
616
|
+
)}
|
|
617
|
+
</Presence>
|
|
618
|
+
);
|
|
619
|
+
}
|
|
620
|
+
```
|
|
621
|
+
|
|
622
|
+
### Scroll-Triggered Animations
|
|
623
|
+
|
|
624
|
+
```tsx
|
|
625
|
+
import { animate, withSpring, withTiming } from 'react-ui-animate';
|
|
626
|
+
|
|
627
|
+
function FeatureCard({ title, description }) {
|
|
628
|
+
return (
|
|
629
|
+
<animate.div
|
|
630
|
+
style={{
|
|
631
|
+
opacity: 0,
|
|
632
|
+
translateY: 50,
|
|
633
|
+
scale: 0.9,
|
|
634
|
+
}}
|
|
635
|
+
view={{
|
|
636
|
+
opacity: withTiming(1, { duration: 600 }),
|
|
637
|
+
translateY: withSpring(0),
|
|
638
|
+
scale: withSpring(1),
|
|
639
|
+
}}
|
|
640
|
+
viewOptions={{ threshold: 0.3, once: true }}
|
|
641
|
+
>
|
|
642
|
+
<h3>{title}</h3>
|
|
643
|
+
<p>{description}</p>
|
|
644
|
+
</animate.div>
|
|
645
|
+
);
|
|
646
|
+
}
|
|
647
|
+
```
|
|
648
|
+
|
|
649
|
+
### Interactive Button
|
|
650
|
+
|
|
651
|
+
```tsx
|
|
652
|
+
import { animate, withSpring, hoverScale, pressScale } from 'react-ui-animate';
|
|
653
|
+
|
|
654
|
+
<animate.button
|
|
655
|
+
style={{
|
|
656
|
+
padding: '12px 24px',
|
|
657
|
+
backgroundColor: '#3399ff',
|
|
658
|
+
color: 'white',
|
|
659
|
+
border: 'none',
|
|
660
|
+
borderRadius: 8,
|
|
661
|
+
cursor: 'pointer',
|
|
662
|
+
}}
|
|
663
|
+
hover={hoverScale}
|
|
664
|
+
press={pressScale}
|
|
665
|
+
>
|
|
666
|
+
Click Me
|
|
667
|
+
</animate.button>
|
|
198
668
|
```
|
|
199
669
|
|
|
670
|
+
---
|
|
671
|
+
|
|
672
|
+
## API Reference
|
|
673
|
+
|
|
674
|
+
### Animation Descriptors
|
|
675
|
+
|
|
676
|
+
| Descriptor | Description | Options |
|
|
677
|
+
|------------|-------------|---------|
|
|
678
|
+
| `withSpring(to, options?)` | Physics-based spring | `stiffness`, `damping`, `mass`, `from`, callbacks |
|
|
679
|
+
| `withTiming(to, options?)` | Time-based animation | `duration`, `easing`, `from`, callbacks |
|
|
680
|
+
| `withDecay(options)` | Momentum decay | `velocity`, `clamp`, callbacks |
|
|
681
|
+
| `withSequence(animations)` | Run sequentially | `animations` array, callbacks |
|
|
682
|
+
| `withLoop(animation, iterations)` | Repeat animation | `iterations` (0 = infinite), callbacks |
|
|
683
|
+
| `withDelay(ms)` | Add delay | `delay` in milliseconds |
|
|
684
|
+
|
|
685
|
+
### Animate Component Props
|
|
686
|
+
|
|
687
|
+
| Prop | Type | Description |
|
|
688
|
+
|------|------|-------------|
|
|
689
|
+
| `animate` | `AnimateProp` | Animations on mount/update |
|
|
690
|
+
| `exit` | `AnimateProp` | Animations on unmount (requires `Presence`) |
|
|
691
|
+
| `hover` | `AnimateProp` | Animations on hover |
|
|
692
|
+
| `press` | `AnimateProp` | Animations on press (mousedown/touchstart) |
|
|
693
|
+
| `focus` | `AnimateProp` | Animations on focus |
|
|
694
|
+
| `view` | `AnimateProp` | Animations when entering viewport |
|
|
695
|
+
| `viewOptions` | `UseInViewOptions` | IntersectionObserver options |
|
|
696
|
+
|
|
697
|
+
### Callbacks
|
|
698
|
+
|
|
699
|
+
All descriptors support optional callbacks:
|
|
700
|
+
|
|
701
|
+
```tsx
|
|
702
|
+
withSpring(100, {
|
|
703
|
+
onStart: () => console.log('Started'),
|
|
704
|
+
onChange: (value) => console.log('Value:', value),
|
|
705
|
+
onComplete: () => console.log('Completed'),
|
|
706
|
+
})
|
|
707
|
+
```
|
|
708
|
+
|
|
709
|
+
---
|
|
710
|
+
|
|
711
|
+
## TypeScript Support
|
|
712
|
+
|
|
713
|
+
Full TypeScript support is included. All components, hooks, and utilities are fully typed.
|
|
714
|
+
|
|
715
|
+
---
|
|
716
|
+
|
|
717
|
+
## Performance
|
|
718
|
+
|
|
719
|
+
- Animations run on the GPU when possible
|
|
720
|
+
- Automatic batching of style updates
|
|
721
|
+
- Optimized re-renders
|
|
722
|
+
- Tree-shakeable exports
|
|
723
|
+
|
|
724
|
+
---
|
|
725
|
+
|
|
726
|
+
## Browser Support
|
|
727
|
+
|
|
728
|
+
- Chrome/Edge (latest)
|
|
729
|
+
- Firefox (latest)
|
|
730
|
+
- Safari (latest)
|
|
731
|
+
- Mobile browsers (iOS Safari, Chrome Mobile)
|
|
732
|
+
|
|
733
|
+
---
|
|
734
|
+
|
|
200
735
|
## Documentation
|
|
201
736
|
|
|
202
|
-
For detailed documentation and examples, visit the official
|
|
737
|
+
For detailed documentation and more examples, visit the [official documentation](https://react-ui-animate.js.org/).
|
|
738
|
+
|
|
739
|
+
---
|
|
203
740
|
|
|
204
741
|
## License
|
|
205
742
|
|
|
206
|
-
|
|
743
|
+
MIT © [Dipesh Rai](https://github.com/dipeshrai123)
|