react-native-puff-pop 1.0.1 → 1.0.2
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 +1 -0
- package/README.md +21 -0
- package/lib/module/index.js +67 -8
- package/lib/typescript/src/index.d.ts +13 -1
- package/package.json +1 -1
- package/src/index.tsx +87 -7
package/LICENSE
CHANGED
package/README.md
CHANGED
|
@@ -133,6 +133,25 @@ function App() {
|
|
|
133
133
|
}
|
|
134
134
|
```
|
|
135
135
|
|
|
136
|
+
### Loop Animation
|
|
137
|
+
|
|
138
|
+
```tsx
|
|
139
|
+
// Loop infinitely
|
|
140
|
+
<PuffPop effect="rotate" loop={true}>
|
|
141
|
+
<LoadingSpinner />
|
|
142
|
+
</PuffPop>
|
|
143
|
+
|
|
144
|
+
// Loop 3 times
|
|
145
|
+
<PuffPop effect="bounce" loop={3}>
|
|
146
|
+
<NotificationBadge />
|
|
147
|
+
</PuffPop>
|
|
148
|
+
|
|
149
|
+
// Loop with delay between iterations
|
|
150
|
+
<PuffPop effect="scale" loop={true} loopDelay={500}>
|
|
151
|
+
<PulsingDot />
|
|
152
|
+
</PuffPop>
|
|
153
|
+
```
|
|
154
|
+
|
|
136
155
|
## Props
|
|
137
156
|
|
|
138
157
|
| Prop | Type | Default | Description |
|
|
@@ -147,6 +166,8 @@ function App() {
|
|
|
147
166
|
| `animateOnMount` | `boolean` | `true` | Animate when component mounts |
|
|
148
167
|
| `onAnimationComplete` | `() => void` | - | Callback when animation completes |
|
|
149
168
|
| `style` | `ViewStyle` | - | Custom container style |
|
|
169
|
+
| `loop` | `boolean \| number` | `false` | Loop animation (true=infinite, number=times) |
|
|
170
|
+
| `loopDelay` | `number` | `0` | Delay between loop iterations in ms |
|
|
150
171
|
|
|
151
172
|
### Animation Effects (`PuffPopEffect`)
|
|
152
173
|
|
package/lib/module/index.js
CHANGED
|
@@ -25,7 +25,7 @@ function getEasing(type) {
|
|
|
25
25
|
/**
|
|
26
26
|
* PuffPop - Animate children with beautiful entrance effects
|
|
27
27
|
*/
|
|
28
|
-
export function PuffPop({ children, effect = 'scale', duration = 400, delay = 0, easing = 'easeOut', skeleton = true, visible = true, onAnimationComplete, style, animateOnMount = true, }) {
|
|
28
|
+
export function PuffPop({ children, effect = 'scale', duration = 400, delay = 0, easing = 'easeOut', skeleton = true, visible = true, onAnimationComplete, style, animateOnMount = true, loop = false, loopDelay = 0, }) {
|
|
29
29
|
// Animation values
|
|
30
30
|
const opacity = useRef(new Animated.Value(animateOnMount ? 0 : 1)).current;
|
|
31
31
|
const scale = useRef(new Animated.Value(animateOnMount ? getInitialScale(effect) : 1)).current;
|
|
@@ -36,6 +36,7 @@ export function PuffPop({ children, effect = 'scale', duration = 400, delay = 0,
|
|
|
36
36
|
const [measuredHeight, setMeasuredHeight] = useState(null);
|
|
37
37
|
const animatedHeight = useRef(new Animated.Value(0)).current;
|
|
38
38
|
const hasAnimated = useRef(false);
|
|
39
|
+
const loopAnimationRef = useRef(null);
|
|
39
40
|
// Handle layout measurement for non-skeleton mode
|
|
40
41
|
const onLayout = useCallback((event) => {
|
|
41
42
|
if (!skeleton && measuredHeight === null) {
|
|
@@ -105,18 +106,66 @@ export function PuffPop({ children, effect = 'scale', duration = 400, delay = 0,
|
|
|
105
106
|
}
|
|
106
107
|
// Run animations with delay
|
|
107
108
|
const parallelAnimation = Animated.parallel(animations);
|
|
109
|
+
// Reset values function for looping
|
|
110
|
+
const resetValues = () => {
|
|
111
|
+
opacity.setValue(0);
|
|
112
|
+
scale.setValue(getInitialScale(effect));
|
|
113
|
+
rotate.setValue(getInitialRotate(effect));
|
|
114
|
+
translateX.setValue(getInitialTranslateX(effect));
|
|
115
|
+
translateY.setValue(getInitialTranslateY(effect));
|
|
116
|
+
if (!skeleton && measuredHeight !== null) {
|
|
117
|
+
animatedHeight.setValue(0);
|
|
118
|
+
}
|
|
119
|
+
};
|
|
120
|
+
// Build the animation sequence
|
|
121
|
+
let animation;
|
|
108
122
|
if (delay > 0) {
|
|
109
|
-
Animated.sequence([
|
|
123
|
+
animation = Animated.sequence([
|
|
110
124
|
Animated.delay(delay),
|
|
111
125
|
parallelAnimation,
|
|
112
|
-
])
|
|
113
|
-
if (toVisible && onAnimationComplete) {
|
|
114
|
-
onAnimationComplete();
|
|
115
|
-
}
|
|
116
|
-
});
|
|
126
|
+
]);
|
|
117
127
|
}
|
|
118
128
|
else {
|
|
119
|
-
|
|
129
|
+
animation = parallelAnimation;
|
|
130
|
+
}
|
|
131
|
+
// Handle loop option
|
|
132
|
+
if (toVisible && loop) {
|
|
133
|
+
// Stop any existing loop animation
|
|
134
|
+
if (loopAnimationRef.current) {
|
|
135
|
+
loopAnimationRef.current.stop();
|
|
136
|
+
}
|
|
137
|
+
const loopCount = typeof loop === 'number' ? loop : -1;
|
|
138
|
+
let currentIteration = 0;
|
|
139
|
+
const runLoop = () => {
|
|
140
|
+
resetValues();
|
|
141
|
+
animation.start(({ finished }) => {
|
|
142
|
+
if (finished) {
|
|
143
|
+
currentIteration++;
|
|
144
|
+
if (loopCount === -1 || currentIteration < loopCount) {
|
|
145
|
+
// Add delay between loops if specified
|
|
146
|
+
if (loopDelay > 0) {
|
|
147
|
+
setTimeout(runLoop, loopDelay);
|
|
148
|
+
}
|
|
149
|
+
else {
|
|
150
|
+
runLoop();
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
else if (onAnimationComplete) {
|
|
154
|
+
onAnimationComplete();
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
});
|
|
158
|
+
};
|
|
159
|
+
// Store reference and start
|
|
160
|
+
runLoop();
|
|
161
|
+
}
|
|
162
|
+
else {
|
|
163
|
+
// Stop any existing loop animation
|
|
164
|
+
if (loopAnimationRef.current) {
|
|
165
|
+
loopAnimationRef.current.stop();
|
|
166
|
+
loopAnimationRef.current = null;
|
|
167
|
+
}
|
|
168
|
+
animation.start(() => {
|
|
120
169
|
if (toVisible && onAnimationComplete) {
|
|
121
170
|
onAnimationComplete();
|
|
122
171
|
}
|
|
@@ -136,6 +185,8 @@ export function PuffPop({ children, effect = 'scale', duration = 400, delay = 0,
|
|
|
136
185
|
translateX,
|
|
137
186
|
translateY,
|
|
138
187
|
animatedHeight,
|
|
188
|
+
loop,
|
|
189
|
+
loopDelay,
|
|
139
190
|
]);
|
|
140
191
|
// Handle initial mount animation
|
|
141
192
|
useEffect(() => {
|
|
@@ -150,6 +201,14 @@ export function PuffPop({ children, effect = 'scale', duration = 400, delay = 0,
|
|
|
150
201
|
animate(visible);
|
|
151
202
|
}
|
|
152
203
|
}, [visible, animate]);
|
|
204
|
+
// Cleanup loop animation on unmount
|
|
205
|
+
useEffect(() => {
|
|
206
|
+
return () => {
|
|
207
|
+
if (loopAnimationRef.current) {
|
|
208
|
+
loopAnimationRef.current.stop();
|
|
209
|
+
}
|
|
210
|
+
};
|
|
211
|
+
}, []);
|
|
153
212
|
// For non-skeleton mode, measure first
|
|
154
213
|
if (!skeleton && measuredHeight === null) {
|
|
155
214
|
return (_jsx(View, { style: styles.measureContainer, onLayout: onLayout, children: _jsx(View, { style: styles.hidden, children: children }) }));
|
|
@@ -57,9 +57,21 @@ export interface PuffPopProps {
|
|
|
57
57
|
* @default true
|
|
58
58
|
*/
|
|
59
59
|
animateOnMount?: boolean;
|
|
60
|
+
/**
|
|
61
|
+
* Loop the animation
|
|
62
|
+
* - true: loop infinitely
|
|
63
|
+
* - number: loop specific number of times
|
|
64
|
+
* @default false
|
|
65
|
+
*/
|
|
66
|
+
loop?: boolean | number;
|
|
67
|
+
/**
|
|
68
|
+
* Delay between loop iterations in milliseconds
|
|
69
|
+
* @default 0
|
|
70
|
+
*/
|
|
71
|
+
loopDelay?: number;
|
|
60
72
|
}
|
|
61
73
|
/**
|
|
62
74
|
* PuffPop - Animate children with beautiful entrance effects
|
|
63
75
|
*/
|
|
64
|
-
export declare function PuffPop({ children, effect, duration, delay, easing, skeleton, visible, onAnimationComplete, style, animateOnMount, }: PuffPopProps): ReactElement;
|
|
76
|
+
export declare function PuffPop({ children, effect, duration, delay, easing, skeleton, visible, onAnimationComplete, style, animateOnMount, loop, loopDelay, }: PuffPopProps): ReactElement;
|
|
65
77
|
export default PuffPop;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "react-native-puff-pop",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.2",
|
|
4
4
|
"description": "A React Native animation library for revealing children components with beautiful puff and pop effects",
|
|
5
5
|
"main": "./lib/module/index.js",
|
|
6
6
|
"types": "./lib/typescript/src/index.d.ts",
|
package/src/index.tsx
CHANGED
|
@@ -101,6 +101,20 @@ export interface PuffPopProps {
|
|
|
101
101
|
* @default true
|
|
102
102
|
*/
|
|
103
103
|
animateOnMount?: boolean;
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Loop the animation
|
|
107
|
+
* - true: loop infinitely
|
|
108
|
+
* - number: loop specific number of times
|
|
109
|
+
* @default false
|
|
110
|
+
*/
|
|
111
|
+
loop?: boolean | number;
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Delay between loop iterations in milliseconds
|
|
115
|
+
* @default 0
|
|
116
|
+
*/
|
|
117
|
+
loopDelay?: number;
|
|
104
118
|
}
|
|
105
119
|
|
|
106
120
|
/**
|
|
@@ -139,6 +153,8 @@ export function PuffPop({
|
|
|
139
153
|
onAnimationComplete,
|
|
140
154
|
style,
|
|
141
155
|
animateOnMount = true,
|
|
156
|
+
loop = false,
|
|
157
|
+
loopDelay = 0,
|
|
142
158
|
}: PuffPopProps): ReactElement {
|
|
143
159
|
// Animation values
|
|
144
160
|
const opacity = useRef(new Animated.Value(animateOnMount ? 0 : 1)).current;
|
|
@@ -151,6 +167,7 @@ export function PuffPop({
|
|
|
151
167
|
const [measuredHeight, setMeasuredHeight] = useState<number | null>(null);
|
|
152
168
|
const animatedHeight = useRef(new Animated.Value(0)).current;
|
|
153
169
|
const hasAnimated = useRef(false);
|
|
170
|
+
const loopAnimationRef = useRef<Animated.CompositeAnimation | null>(null);
|
|
154
171
|
|
|
155
172
|
// Handle layout measurement for non-skeleton mode
|
|
156
173
|
const onLayout = useCallback(
|
|
@@ -246,18 +263,70 @@ export function PuffPop({
|
|
|
246
263
|
|
|
247
264
|
// Run animations with delay
|
|
248
265
|
const parallelAnimation = Animated.parallel(animations);
|
|
266
|
+
|
|
267
|
+
// Reset values function for looping
|
|
268
|
+
const resetValues = () => {
|
|
269
|
+
opacity.setValue(0);
|
|
270
|
+
scale.setValue(getInitialScale(effect));
|
|
271
|
+
rotate.setValue(getInitialRotate(effect));
|
|
272
|
+
translateX.setValue(getInitialTranslateX(effect));
|
|
273
|
+
translateY.setValue(getInitialTranslateY(effect));
|
|
274
|
+
if (!skeleton && measuredHeight !== null) {
|
|
275
|
+
animatedHeight.setValue(0);
|
|
276
|
+
}
|
|
277
|
+
};
|
|
278
|
+
|
|
279
|
+
// Build the animation sequence
|
|
280
|
+
let animation: Animated.CompositeAnimation;
|
|
249
281
|
|
|
250
282
|
if (delay > 0) {
|
|
251
|
-
Animated.sequence([
|
|
283
|
+
animation = Animated.sequence([
|
|
252
284
|
Animated.delay(delay),
|
|
253
285
|
parallelAnimation,
|
|
254
|
-
])
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
286
|
+
]);
|
|
287
|
+
} else {
|
|
288
|
+
animation = parallelAnimation;
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
// Handle loop option
|
|
292
|
+
if (toVisible && loop) {
|
|
293
|
+
// Stop any existing loop animation
|
|
294
|
+
if (loopAnimationRef.current) {
|
|
295
|
+
loopAnimationRef.current.stop();
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
const loopCount = typeof loop === 'number' ? loop : -1;
|
|
299
|
+
let currentIteration = 0;
|
|
300
|
+
|
|
301
|
+
const runLoop = () => {
|
|
302
|
+
resetValues();
|
|
303
|
+
animation.start(({ finished }) => {
|
|
304
|
+
if (finished) {
|
|
305
|
+
currentIteration++;
|
|
306
|
+
if (loopCount === -1 || currentIteration < loopCount) {
|
|
307
|
+
// Add delay between loops if specified
|
|
308
|
+
if (loopDelay > 0) {
|
|
309
|
+
setTimeout(runLoop, loopDelay);
|
|
310
|
+
} else {
|
|
311
|
+
runLoop();
|
|
312
|
+
}
|
|
313
|
+
} else if (onAnimationComplete) {
|
|
314
|
+
onAnimationComplete();
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
});
|
|
318
|
+
};
|
|
319
|
+
|
|
320
|
+
// Store reference and start
|
|
321
|
+
runLoop();
|
|
259
322
|
} else {
|
|
260
|
-
|
|
323
|
+
// Stop any existing loop animation
|
|
324
|
+
if (loopAnimationRef.current) {
|
|
325
|
+
loopAnimationRef.current.stop();
|
|
326
|
+
loopAnimationRef.current = null;
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
animation.start(() => {
|
|
261
330
|
if (toVisible && onAnimationComplete) {
|
|
262
331
|
onAnimationComplete();
|
|
263
332
|
}
|
|
@@ -278,6 +347,8 @@ export function PuffPop({
|
|
|
278
347
|
translateX,
|
|
279
348
|
translateY,
|
|
280
349
|
animatedHeight,
|
|
350
|
+
loop,
|
|
351
|
+
loopDelay,
|
|
281
352
|
]
|
|
282
353
|
);
|
|
283
354
|
|
|
@@ -296,6 +367,15 @@ export function PuffPop({
|
|
|
296
367
|
}
|
|
297
368
|
}, [visible, animate]);
|
|
298
369
|
|
|
370
|
+
// Cleanup loop animation on unmount
|
|
371
|
+
useEffect(() => {
|
|
372
|
+
return () => {
|
|
373
|
+
if (loopAnimationRef.current) {
|
|
374
|
+
loopAnimationRef.current.stop();
|
|
375
|
+
}
|
|
376
|
+
};
|
|
377
|
+
}, []);
|
|
378
|
+
|
|
299
379
|
// For non-skeleton mode, measure first
|
|
300
380
|
if (!skeleton && measuredHeight === null) {
|
|
301
381
|
return (
|