react-native-glitter 1.0.3 → 1.0.5
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 +42 -0
- package/lib/module/index.js +22 -4
- package/lib/typescript/src/index.d.ts +19 -3
- package/package.json +1 -1
- package/src/index.tsx +76 -19
package/README.md
CHANGED
|
@@ -167,6 +167,9 @@ function ControlledGlitter() {
|
|
|
167
167
|
| `iterations` | `number` | `-1` | Number of animation cycles (-1 for infinite) |
|
|
168
168
|
| `onAnimationStart` | `() => void` | - | Callback when animation starts |
|
|
169
169
|
| `onAnimationComplete` | `() => void` | - | Callback when all iterations complete |
|
|
170
|
+
| `testID` | `string` | - | Test ID for e2e testing |
|
|
171
|
+
| `accessibilityLabel` | `string` | - | Accessibility label for screen readers |
|
|
172
|
+
| `accessible` | `boolean` | `true` | Whether the component is accessible |
|
|
170
173
|
|
|
171
174
|
## Examples
|
|
172
175
|
|
|
@@ -261,6 +264,44 @@ function ControlledGlitter() {
|
|
|
261
264
|
</Glitter>
|
|
262
265
|
```
|
|
263
266
|
|
|
267
|
+
## Ref API
|
|
268
|
+
|
|
269
|
+
You can control the animation programmatically using a ref:
|
|
270
|
+
|
|
271
|
+
```tsx
|
|
272
|
+
import { useRef } from 'react';
|
|
273
|
+
import { Glitter, type GlitterRef } from 'react-native-glitter';
|
|
274
|
+
|
|
275
|
+
function MyComponent() {
|
|
276
|
+
const glitterRef = useRef<GlitterRef>(null);
|
|
277
|
+
|
|
278
|
+
const handleStart = () => glitterRef.current?.start();
|
|
279
|
+
const handleStop = () => glitterRef.current?.stop();
|
|
280
|
+
const handleRestart = () => glitterRef.current?.restart();
|
|
281
|
+
const checkStatus = () => console.log(glitterRef.current?.isAnimating());
|
|
282
|
+
|
|
283
|
+
return (
|
|
284
|
+
<>
|
|
285
|
+
<Glitter ref={glitterRef} active={false}>
|
|
286
|
+
<View style={styles.box} />
|
|
287
|
+
</Glitter>
|
|
288
|
+
<Button title="Start" onPress={handleStart} />
|
|
289
|
+
<Button title="Stop" onPress={handleStop} />
|
|
290
|
+
<Button title="Restart" onPress={handleRestart} />
|
|
291
|
+
</>
|
|
292
|
+
);
|
|
293
|
+
}
|
|
294
|
+
```
|
|
295
|
+
|
|
296
|
+
### Ref Methods
|
|
297
|
+
|
|
298
|
+
| Method | Return | Description |
|
|
299
|
+
|--------|--------|-------------|
|
|
300
|
+
| `start()` | `void` | Start the shimmer animation |
|
|
301
|
+
| `stop()` | `void` | Stop the shimmer animation |
|
|
302
|
+
| `restart()` | `void` | Restart the animation from the beginning |
|
|
303
|
+
| `isAnimating()` | `boolean` | Check if animation is currently running |
|
|
304
|
+
|
|
264
305
|
## TypeScript
|
|
265
306
|
|
|
266
307
|
This library is written in TypeScript and includes type definitions:
|
|
@@ -269,6 +310,7 @@ This library is written in TypeScript and includes type definitions:
|
|
|
269
310
|
import {
|
|
270
311
|
Glitter,
|
|
271
312
|
type GlitterProps,
|
|
313
|
+
type GlitterRef,
|
|
272
314
|
type GlitterMode,
|
|
273
315
|
type GlitterPosition,
|
|
274
316
|
type GlitterDirection,
|
package/lib/module/index.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { useEffect, useRef, useState, useCallback, useMemo, memo, } from 'react';
|
|
2
|
+
import { useEffect, useRef, useState, useCallback, useMemo, memo, useImperativeHandle, forwardRef, } from 'react';
|
|
3
3
|
import { View, Animated, StyleSheet, Easing, } from 'react-native';
|
|
4
4
|
function generateGlitterOpacities(count, peak = 1) {
|
|
5
5
|
const opacities = [];
|
|
@@ -52,7 +52,7 @@ const HEIGHT_MULTIPLIER = 1.5;
|
|
|
52
52
|
const NORMAL_FADE_RATIO = (HEIGHT_MULTIPLIER - 1) / HEIGHT_MULTIPLIER / 2;
|
|
53
53
|
const ANIMATED_SEGMENTS = generateVerticalSegments(0.25);
|
|
54
54
|
const NORMAL_SEGMENTS = generateVerticalSegments(NORMAL_FADE_RATIO);
|
|
55
|
-
function GlitterComponent({ children, duration = 1500, delay = 400, color = 'rgba(255, 255, 255, 0.8)', angle = 20, shimmerWidth = 60, active = true, style, easing, mode = 'normal', position = 'center', direction = 'left-to-right', iterations = -1, onAnimationStart, onAnimationComplete, }) {
|
|
55
|
+
function GlitterComponent({ children, duration = 1500, delay = 400, color = 'rgba(255, 255, 255, 0.8)', angle = 20, shimmerWidth = 60, active = true, style, easing, mode = 'normal', position = 'center', direction = 'left-to-right', iterations = -1, onAnimationStart, onAnimationComplete, testID, accessibilityLabel, accessible = true, }, ref) {
|
|
56
56
|
const animatedValue = useRef(new Animated.Value(0)).current;
|
|
57
57
|
const [containerWidth, setContainerWidth] = useState(0);
|
|
58
58
|
const [containerHeight, setContainerHeight] = useState(0);
|
|
@@ -68,6 +68,23 @@ function GlitterComponent({ children, duration = 1500, delay = 400, color = 'rgb
|
|
|
68
68
|
currentIterationRef.current?.stop();
|
|
69
69
|
currentIterationRef.current = null;
|
|
70
70
|
}, []);
|
|
71
|
+
const restartAnimation = useCallback(() => {
|
|
72
|
+
stopAnimation();
|
|
73
|
+
animatedValue.setValue(0);
|
|
74
|
+
// Trigger re-render to start animation
|
|
75
|
+
setContainerWidth((prev) => prev);
|
|
76
|
+
}, [stopAnimation, animatedValue]);
|
|
77
|
+
useImperativeHandle(ref, () => ({
|
|
78
|
+
start: () => {
|
|
79
|
+
if (!isAnimatingRef.current && containerWidth > 0) {
|
|
80
|
+
// Force start by triggering the effect
|
|
81
|
+
setContainerWidth((prev) => prev);
|
|
82
|
+
}
|
|
83
|
+
},
|
|
84
|
+
stop: stopAnimation,
|
|
85
|
+
restart: restartAnimation,
|
|
86
|
+
isAnimating: () => isAnimatingRef.current,
|
|
87
|
+
}), [stopAnimation, restartAnimation, containerWidth]);
|
|
71
88
|
const startAnimation = useCallback(() => {
|
|
72
89
|
if (!active || containerWidth === 0)
|
|
73
90
|
return;
|
|
@@ -198,7 +215,7 @@ function GlitterComponent({ children, duration = 1500, delay = 400, color = 'rgb
|
|
|
198
215
|
: [],
|
|
199
216
|
},
|
|
200
217
|
], [layerWidth, lineHeight, isAnimated, transformOriginOffset, scaleY]);
|
|
201
|
-
return (_jsxs(View, { style: [styles.container, style], onLayout: onLayout, children: [children, active && containerWidth > 0 && containerHeight > 0 && (_jsx(Animated.View, { style: shimmerContainerStyle, pointerEvents: "none", children: _jsx(View, { style: rotationWrapperStyle, children: shimmerLayers.map((layer, layerIndex) => (_jsx(Animated.View, { style: getShimmerLineStyle(layer.position), children: segments.map((segment, vIndex) => (_jsx(View, { style: [
|
|
218
|
+
return (_jsxs(View, { style: [styles.container, style], onLayout: onLayout, testID: testID, accessibilityLabel: accessibilityLabel, accessible: accessible, children: [children, active && containerWidth > 0 && containerHeight > 0 && (_jsx(Animated.View, { style: shimmerContainerStyle, pointerEvents: "none", children: _jsx(View, { style: rotationWrapperStyle, children: shimmerLayers.map((layer, layerIndex) => (_jsx(Animated.View, { style: getShimmerLineStyle(layer.position), children: segments.map((segment, vIndex) => (_jsx(View, { style: [
|
|
202
219
|
styles.segment,
|
|
203
220
|
{
|
|
204
221
|
height: lineHeight * segment.heightRatio,
|
|
@@ -231,5 +248,6 @@ const styles = StyleSheet.create({
|
|
|
231
248
|
width: '100%',
|
|
232
249
|
},
|
|
233
250
|
});
|
|
234
|
-
|
|
251
|
+
const ForwardedGlitter = forwardRef(GlitterComponent);
|
|
252
|
+
export const Glitter = memo(ForwardedGlitter);
|
|
235
253
|
export default Glitter;
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { type ReactNode
|
|
1
|
+
import { type ReactNode } from 'react';
|
|
2
2
|
import { type StyleProp, type ViewStyle } from 'react-native';
|
|
3
3
|
export type GlitterMode = 'normal' | 'expand' | 'shrink';
|
|
4
4
|
export type GlitterPosition = 'top' | 'center' | 'bottom';
|
|
@@ -19,7 +19,23 @@ export interface GlitterProps {
|
|
|
19
19
|
iterations?: number;
|
|
20
20
|
onAnimationStart?: () => void;
|
|
21
21
|
onAnimationComplete?: () => void;
|
|
22
|
+
/** Test ID for e2e testing */
|
|
23
|
+
testID?: string;
|
|
24
|
+
/** Accessibility label for screen readers */
|
|
25
|
+
accessibilityLabel?: string;
|
|
26
|
+
/** Whether the component is accessible (default: true) */
|
|
27
|
+
accessible?: boolean;
|
|
22
28
|
}
|
|
23
|
-
|
|
24
|
-
export
|
|
29
|
+
/** Ref methods exposed by Glitter component */
|
|
30
|
+
export interface GlitterRef {
|
|
31
|
+
/** Start the shimmer animation */
|
|
32
|
+
start: () => void;
|
|
33
|
+
/** Stop the shimmer animation */
|
|
34
|
+
stop: () => void;
|
|
35
|
+
/** Restart the shimmer animation from the beginning */
|
|
36
|
+
restart: () => void;
|
|
37
|
+
/** Check if animation is currently running */
|
|
38
|
+
isAnimating: () => boolean;
|
|
39
|
+
}
|
|
40
|
+
export declare const Glitter: import("react").NamedExoticComponent<GlitterProps & import("react").RefAttributes<GlitterRef>>;
|
|
25
41
|
export default Glitter;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "react-native-glitter",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.5",
|
|
4
4
|
"description": "A beautiful shimmer/glitter effect component for React Native. Add a sparkling diagonal shine animation to any component!",
|
|
5
5
|
"main": "./lib/module/index.js",
|
|
6
6
|
"types": "./lib/typescript/src/index.d.ts",
|
package/src/index.tsx
CHANGED
|
@@ -5,8 +5,11 @@ import {
|
|
|
5
5
|
useCallback,
|
|
6
6
|
useMemo,
|
|
7
7
|
memo,
|
|
8
|
+
useImperativeHandle,
|
|
9
|
+
forwardRef,
|
|
8
10
|
type ReactNode,
|
|
9
11
|
type ReactElement,
|
|
12
|
+
type ForwardedRef,
|
|
10
13
|
} from 'react';
|
|
11
14
|
import {
|
|
12
15
|
View,
|
|
@@ -40,6 +43,24 @@ export interface GlitterProps {
|
|
|
40
43
|
iterations?: number;
|
|
41
44
|
onAnimationStart?: () => void;
|
|
42
45
|
onAnimationComplete?: () => void;
|
|
46
|
+
/** Test ID for e2e testing */
|
|
47
|
+
testID?: string;
|
|
48
|
+
/** Accessibility label for screen readers */
|
|
49
|
+
accessibilityLabel?: string;
|
|
50
|
+
/** Whether the component is accessible (default: true) */
|
|
51
|
+
accessible?: boolean;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/** Ref methods exposed by Glitter component */
|
|
55
|
+
export interface GlitterRef {
|
|
56
|
+
/** Start the shimmer animation */
|
|
57
|
+
start: () => void;
|
|
58
|
+
/** Stop the shimmer animation */
|
|
59
|
+
stop: () => void;
|
|
60
|
+
/** Restart the shimmer animation from the beginning */
|
|
61
|
+
restart: () => void;
|
|
62
|
+
/** Check if animation is currently running */
|
|
63
|
+
isAnimating: () => boolean;
|
|
43
64
|
}
|
|
44
65
|
|
|
45
66
|
function generateGlitterOpacities(count: number, peak: number = 1): number[] {
|
|
@@ -108,23 +129,29 @@ const NORMAL_FADE_RATIO = (HEIGHT_MULTIPLIER - 1) / HEIGHT_MULTIPLIER / 2;
|
|
|
108
129
|
const ANIMATED_SEGMENTS = generateVerticalSegments(0.25);
|
|
109
130
|
const NORMAL_SEGMENTS = generateVerticalSegments(NORMAL_FADE_RATIO);
|
|
110
131
|
|
|
111
|
-
function GlitterComponent(
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
132
|
+
function GlitterComponent(
|
|
133
|
+
{
|
|
134
|
+
children,
|
|
135
|
+
duration = 1500,
|
|
136
|
+
delay = 400,
|
|
137
|
+
color = 'rgba(255, 255, 255, 0.8)',
|
|
138
|
+
angle = 20,
|
|
139
|
+
shimmerWidth = 60,
|
|
140
|
+
active = true,
|
|
141
|
+
style,
|
|
142
|
+
easing,
|
|
143
|
+
mode = 'normal',
|
|
144
|
+
position = 'center',
|
|
145
|
+
direction = 'left-to-right',
|
|
146
|
+
iterations = -1,
|
|
147
|
+
onAnimationStart,
|
|
148
|
+
onAnimationComplete,
|
|
149
|
+
testID,
|
|
150
|
+
accessibilityLabel,
|
|
151
|
+
accessible = true,
|
|
152
|
+
}: GlitterProps,
|
|
153
|
+
ref: ForwardedRef<GlitterRef>
|
|
154
|
+
): ReactElement {
|
|
128
155
|
const animatedValue = useRef(new Animated.Value(0)).current;
|
|
129
156
|
const [containerWidth, setContainerWidth] = useState(0);
|
|
130
157
|
const [containerHeight, setContainerHeight] = useState(0);
|
|
@@ -145,6 +172,29 @@ function GlitterComponent({
|
|
|
145
172
|
currentIterationRef.current = null;
|
|
146
173
|
}, []);
|
|
147
174
|
|
|
175
|
+
const restartAnimation = useCallback(() => {
|
|
176
|
+
stopAnimation();
|
|
177
|
+
animatedValue.setValue(0);
|
|
178
|
+
// Trigger re-render to start animation
|
|
179
|
+
setContainerWidth((prev) => prev);
|
|
180
|
+
}, [stopAnimation, animatedValue]);
|
|
181
|
+
|
|
182
|
+
useImperativeHandle(
|
|
183
|
+
ref,
|
|
184
|
+
() => ({
|
|
185
|
+
start: () => {
|
|
186
|
+
if (!isAnimatingRef.current && containerWidth > 0) {
|
|
187
|
+
// Force start by triggering the effect
|
|
188
|
+
setContainerWidth((prev) => prev);
|
|
189
|
+
}
|
|
190
|
+
},
|
|
191
|
+
stop: stopAnimation,
|
|
192
|
+
restart: restartAnimation,
|
|
193
|
+
isAnimating: () => isAnimatingRef.current,
|
|
194
|
+
}),
|
|
195
|
+
[stopAnimation, restartAnimation, containerWidth]
|
|
196
|
+
);
|
|
197
|
+
|
|
148
198
|
const startAnimation = useCallback(() => {
|
|
149
199
|
if (!active || containerWidth === 0) return;
|
|
150
200
|
|
|
@@ -332,7 +382,13 @@ function GlitterComponent({
|
|
|
332
382
|
);
|
|
333
383
|
|
|
334
384
|
return (
|
|
335
|
-
<View
|
|
385
|
+
<View
|
|
386
|
+
style={[styles.container, style]}
|
|
387
|
+
onLayout={onLayout}
|
|
388
|
+
testID={testID}
|
|
389
|
+
accessibilityLabel={accessibilityLabel}
|
|
390
|
+
accessible={accessible}
|
|
391
|
+
>
|
|
336
392
|
{children}
|
|
337
393
|
{active && containerWidth > 0 && containerHeight > 0 && (
|
|
338
394
|
<Animated.View style={shimmerContainerStyle} pointerEvents="none">
|
|
@@ -389,6 +445,7 @@ const styles = StyleSheet.create({
|
|
|
389
445
|
},
|
|
390
446
|
});
|
|
391
447
|
|
|
392
|
-
|
|
448
|
+
const ForwardedGlitter = forwardRef(GlitterComponent);
|
|
449
|
+
export const Glitter = memo(ForwardedGlitter);
|
|
393
450
|
|
|
394
451
|
export default Glitter;
|