react-native-skelo 0.0.2 → 0.0.3
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 -1
- package/README.md +6 -4
- package/lib/commonjs/animations/PulseAnimation.js +2 -26
- package/lib/commonjs/animations/PulseAnimation.js.map +1 -1
- package/lib/commonjs/animations/ShimmerAnimation.js +25 -55
- package/lib/commonjs/animations/ShimmerAnimation.js.map +1 -1
- package/lib/module/animations/PulseAnimation.js +2 -26
- package/lib/module/animations/PulseAnimation.js.map +1 -1
- package/lib/module/animations/ShimmerAnimation.js +26 -56
- package/lib/module/animations/ShimmerAnimation.js.map +1 -1
- package/lib/typescript/animations/PulseAnimation.d.ts +0 -44
- package/lib/typescript/animations/PulseAnimation.d.ts.map +1 -1
- package/lib/typescript/animations/ShimmerAnimation.d.ts +0 -44
- package/lib/typescript/animations/ShimmerAnimation.d.ts.map +1 -1
- package/package.json +2 -4
- package/src/animations/PulseAnimation.tsx +5 -67
- package/src/animations/ShimmerAnimation.tsx +38 -106
package/LICENSE
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
MIT License
|
|
2
2
|
|
|
3
|
-
Copyright (c) 2026 Shubhanshu
|
|
3
|
+
Copyright (c) 2026 Shubhanshu Barnwal <connect@shubhanshubb.dev> (https://shubhanshubb.dev)
|
|
4
4
|
|
|
5
5
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
6
|
of this software and associated documentation files (the "Software"), to deal
|
package/README.md
CHANGED
|
@@ -29,11 +29,12 @@ While `loading`, Skelo reads the tree and its styles and renders a matching skel
|
|
|
29
29
|
## Installation
|
|
30
30
|
|
|
31
31
|
```bash
|
|
32
|
-
npm install react-native-skelo
|
|
33
|
-
npm install react-native-reanimated react-native-linear-gradient
|
|
32
|
+
npm install react-native-skelo react-native-reanimated
|
|
34
33
|
```
|
|
35
34
|
|
|
36
|
-
That's all you need for the basic (inline) and `styles` usage.
|
|
35
|
+
That's all you need for the basic (inline) and `styles` usage. The shimmer uses
|
|
36
|
+
React Native's built-in `experimental_backgroundImage` gradient (New
|
|
37
|
+
Architecture) — no gradient library required.
|
|
37
38
|
|
|
38
39
|
### To enable `deep` mode (optional)
|
|
39
40
|
|
|
@@ -160,7 +161,8 @@ Also exported: `withSkeleton`, `SkeletonIgnore`, `StyleSkeleton`, the primitives
|
|
|
160
161
|
|
|
161
162
|
## Requirements
|
|
162
163
|
|
|
163
|
-
- React Native
|
|
164
|
+
- React Native — New Architecture recommended (the shimmer's
|
|
165
|
+
`experimental_backgroundImage` gradient needs it)
|
|
164
166
|
- `react-native-reanimated` ≥ 3
|
|
165
167
|
- `react-reconciler` (optional, for `deep`) — must match your React version
|
|
166
168
|
|
|
@@ -8,23 +8,7 @@ var _react = _interopRequireWildcard(require("react"));
|
|
|
8
8
|
var _reactNative = require("react-native");
|
|
9
9
|
var _reactNativeReanimated = _interopRequireWildcard(require("react-native-reanimated"));
|
|
10
10
|
function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function (e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, default: e }; if (null === e || "object" != typeof e && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (const t in e) "default" !== t && {}.hasOwnProperty.call(e, t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, t)) && (i.get || i.set) ? o(f, t, i) : f[t] = e[t]); return f; })(e, t); }
|
|
11
|
-
|
|
12
|
-
* Pulse animation component using Reanimated 3
|
|
13
|
-
*
|
|
14
|
-
* Creates a subtle breathing/pulsing effect using:
|
|
15
|
-
* - Shared values for performance
|
|
16
|
-
* - Worklets for 60 FPS on UI thread
|
|
17
|
-
* - Opacity animation for pulse effect
|
|
18
|
-
*
|
|
19
|
-
* @example
|
|
20
|
-
* ```tsx
|
|
21
|
-
* <PulseAnimation
|
|
22
|
-
* width={200}
|
|
23
|
-
* height={20}
|
|
24
|
-
* borderRadius={4}
|
|
25
|
-
* />
|
|
26
|
-
* ```
|
|
27
|
-
*/
|
|
11
|
+
// Breathing opacity loop on the UI thread with Reanimated.
|
|
28
12
|
function PulseAnimation({
|
|
29
13
|
width,
|
|
30
14
|
height,
|
|
@@ -34,24 +18,16 @@ function PulseAnimation({
|
|
|
34
18
|
style,
|
|
35
19
|
children
|
|
36
20
|
}) {
|
|
37
|
-
// Shared value for opacity (0 to 1)
|
|
38
21
|
const opacity = (0, _reactNativeReanimated.useSharedValue)(1);
|
|
39
22
|
(0, _react.useEffect)(() => {
|
|
40
|
-
// Start infinite pulse animation
|
|
41
|
-
// Sequence: fade out -> fade in
|
|
42
23
|
opacity.value = (0, _reactNativeReanimated.withRepeat)((0, _reactNativeReanimated.withSequence)((0, _reactNativeReanimated.withTiming)(0.5, {
|
|
43
24
|
duration: duration / 2,
|
|
44
25
|
easing: _reactNativeReanimated.Easing.inOut(_reactNativeReanimated.Easing.ease)
|
|
45
26
|
}), (0, _reactNativeReanimated.withTiming)(1, {
|
|
46
27
|
duration: duration / 2,
|
|
47
28
|
easing: _reactNativeReanimated.Easing.inOut(_reactNativeReanimated.Easing.ease)
|
|
48
|
-
})), -1,
|
|
49
|
-
// Infinite repeat
|
|
50
|
-
false // Don't reverse
|
|
51
|
-
);
|
|
29
|
+
})), -1, false);
|
|
52
30
|
}, [duration, opacity]);
|
|
53
|
-
|
|
54
|
-
// Animated style for pulse effect
|
|
55
31
|
const animatedStyle = (0, _reactNativeReanimated.useAnimatedStyle)(() => {
|
|
56
32
|
'worklet';
|
|
57
33
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"names":["_react","_interopRequireWildcard","require","_reactNative","_reactNativeReanimated","e","t","WeakMap","r","n","__esModule","o","i","f","__proto__","default","has","get","set","hasOwnProperty","call","Object","defineProperty","getOwnPropertyDescriptor","PulseAnimation","width","height","baseColor","duration","borderRadius","style","children","opacity","useSharedValue","useEffect","value","withRepeat","withSequence","withTiming","easing","Easing","inOut","ease","animatedStyle","useAnimatedStyle","createElement","View","styles","container","backgroundColor","StyleSheet","create","overflow"],"sourceRoot":"../../../src","sources":["animations/PulseAnimation.tsx"],"mappings":";;;;;;AAAA,IAAAA,MAAA,GAAAC,uBAAA,CAAAC,OAAA;AAEA,IAAAC,YAAA,GAAAD,OAAA;AACA,IAAAE,sBAAA,GAAAH,uBAAA,CAAAC,OAAA;AAOiC,SAAAD,wBAAAI,CAAA,EAAAC,CAAA,6BAAAC,OAAA,MAAAC,CAAA,OAAAD,OAAA,IAAAE,CAAA,OAAAF,OAAA,YAAAN,uBAAA,YAAAA,CAAAI,CAAA,EAAAC,CAAA,SAAAA,CAAA,IAAAD,CAAA,IAAAA,CAAA,CAAAK,UAAA,SAAAL,CAAA,MAAAM,CAAA,EAAAC,CAAA,EAAAC,CAAA,KAAAC,SAAA,QAAAC,OAAA,EAAAV,CAAA,iBAAAA,CAAA,uBAAAA,CAAA,yBAAAA,CAAA,SAAAQ,CAAA,MAAAF,CAAA,GAAAL,CAAA,GAAAG,CAAA,GAAAD,CAAA,QAAAG,CAAA,CAAAK,GAAA,CAAAX,CAAA,UAAAM,CAAA,CAAAM,GAAA,CAAAZ,CAAA,GAAAM,CAAA,CAAAO,GAAA,CAAAb,CAAA,EAAAQ,CAAA,gBAAAP,CAAA,IAAAD,CAAA,gBAAAC,CAAA,OAAAa,cAAA,CAAAC,IAAA,CAAAf,CAAA,EAAAC,CAAA,OAAAM,CAAA,IAAAD,CAAA,GAAAU,MAAA,CAAAC,cAAA,KAAAD,MAAA,CAAAE,wBAAA,CAAAlB,CAAA,EAAAC,CAAA,OAAAM,CAAA,CAAAK,GAAA,IAAAL,CAAA,CAAAM,GAAA,IAAAP,CAAA,CAAAE,CAAA,EAAAP,CAAA,EAAAM,CAAA,IAAAC,CAAA,CAAAP,CAAA,IAAAD,CAAA,CAAAC,CAAA,WAAAO,CAAA,KAAAR,CAAA,EAAAC,CAAA;
|
|
1
|
+
{"version":3,"names":["_react","_interopRequireWildcard","require","_reactNative","_reactNativeReanimated","e","t","WeakMap","r","n","__esModule","o","i","f","__proto__","default","has","get","set","hasOwnProperty","call","Object","defineProperty","getOwnPropertyDescriptor","PulseAnimation","width","height","baseColor","duration","borderRadius","style","children","opacity","useSharedValue","useEffect","value","withRepeat","withSequence","withTiming","easing","Easing","inOut","ease","animatedStyle","useAnimatedStyle","createElement","View","styles","container","backgroundColor","StyleSheet","create","overflow"],"sourceRoot":"../../../src","sources":["animations/PulseAnimation.tsx"],"mappings":"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","ignoreList":[]}
|
|
@@ -7,25 +7,10 @@ exports.ShimmerAnimation = ShimmerAnimation;
|
|
|
7
7
|
var _react = _interopRequireWildcard(require("react"));
|
|
8
8
|
var _reactNative = require("react-native");
|
|
9
9
|
var _reactNativeReanimated = _interopRequireWildcard(require("react-native-reanimated"));
|
|
10
|
-
var _reactNativeLinearGradient = require("react-native-linear-gradient");
|
|
11
10
|
function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function (e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, default: e }; if (null === e || "object" != typeof e && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (const t in e) "default" !== t && {}.hasOwnProperty.call(e, t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, t)) && (i.get || i.set) ? o(f, t, i) : f[t] = e[t]); return f; })(e, t); }
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
* Creates a smooth left-to-right shimmer effect using:
|
|
16
|
-
* - Shared values for performance
|
|
17
|
-
* - Worklets for 60 FPS on UI thread
|
|
18
|
-
* - Linear gradient for shimmer effect
|
|
19
|
-
*
|
|
20
|
-
* @example
|
|
21
|
-
* ```tsx
|
|
22
|
-
* <ShimmerAnimation
|
|
23
|
-
* width={200}
|
|
24
|
-
* height={20}
|
|
25
|
-
* borderRadius={4}
|
|
26
|
-
* />
|
|
27
|
-
* ```
|
|
28
|
-
*/
|
|
11
|
+
// Shimmer with no gradient library: the sweeping overlay uses React Native's
|
|
12
|
+
// built-in `experimental_backgroundImage` CSS gradient (New Architecture),
|
|
13
|
+
// animated on the UI thread with Reanimated.
|
|
29
14
|
function ShimmerAnimation({
|
|
30
15
|
width,
|
|
31
16
|
height,
|
|
@@ -36,33 +21,37 @@ function ShimmerAnimation({
|
|
|
36
21
|
style,
|
|
37
22
|
children
|
|
38
23
|
}) {
|
|
39
|
-
// Shared value for animation progress (0 to 1)
|
|
40
24
|
const progress = (0, _reactNativeReanimated.useSharedValue)(0);
|
|
25
|
+
const [measuredWidth, setMeasuredWidth] = (0, _react.useState)(0);
|
|
41
26
|
(0, _react.useEffect)(() => {
|
|
42
|
-
// Start infinite shimmer animation
|
|
43
27
|
progress.value = (0, _reactNativeReanimated.withRepeat)((0, _reactNativeReanimated.withTiming)(1, {
|
|
44
28
|
duration,
|
|
45
29
|
easing: _reactNativeReanimated.Easing.linear
|
|
46
|
-
}), -1,
|
|
47
|
-
// Infinite repeat
|
|
48
|
-
false // Don't reverse
|
|
49
|
-
);
|
|
30
|
+
}), -1, false);
|
|
50
31
|
}, [duration, progress]);
|
|
51
|
-
|
|
52
|
-
|
|
32
|
+
const onLayout = event => {
|
|
33
|
+
const w = event.nativeEvent.layout.width;
|
|
34
|
+
setMeasuredWidth(prev => prev === w ? prev : w);
|
|
35
|
+
};
|
|
36
|
+
const containerWidth = typeof width === 'number' ? width : measuredWidth;
|
|
53
37
|
const animatedStyle = (0, _reactNativeReanimated.useAnimatedStyle)(() => {
|
|
54
38
|
'worklet';
|
|
55
39
|
|
|
56
|
-
|
|
57
|
-
// Interpolate from -100% to 100% to create left-to-right sweep
|
|
58
|
-
const translateX = (0, _reactNativeReanimated.interpolate)(progress.value, [0, 1], [-1, 1]);
|
|
40
|
+
const translateX = (0, _reactNativeReanimated.interpolate)(progress.value, [0, 1], [-containerWidth, containerWidth]);
|
|
59
41
|
return {
|
|
60
42
|
transform: [{
|
|
61
|
-
translateX
|
|
43
|
+
translateX
|
|
62
44
|
}]
|
|
63
45
|
};
|
|
64
|
-
}, [
|
|
46
|
+
}, [containerWidth]);
|
|
47
|
+
|
|
48
|
+
// `experimental_backgroundImage` isn't in the ViewStyle types yet.
|
|
49
|
+
const gradientStyle = {
|
|
50
|
+
..._reactNative.StyleSheet.absoluteFillObject,
|
|
51
|
+
experimental_backgroundImage: `linear-gradient(to right, ${baseColor}, ${highlightColor}, ${baseColor})`
|
|
52
|
+
};
|
|
65
53
|
return /*#__PURE__*/_react.default.createElement(_reactNative.View, {
|
|
54
|
+
onLayout: onLayout,
|
|
66
55
|
style: [styles.container, {
|
|
67
56
|
width,
|
|
68
57
|
height,
|
|
@@ -70,34 +59,15 @@ function ShimmerAnimation({
|
|
|
70
59
|
backgroundColor: baseColor
|
|
71
60
|
}, style]
|
|
72
61
|
}, /*#__PURE__*/_react.default.createElement(_reactNativeReanimated.default.View, {
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
},
|
|
78
|
-
style: [styles.shimmer, {
|
|
79
|
-
width: typeof width === 'number' ? width : '100%',
|
|
80
|
-
height: '100%'
|
|
81
|
-
}, animatedStyle]
|
|
82
|
-
}, /*#__PURE__*/_react.default.createElement(_reactNativeLinearGradient.LinearGradient, {
|
|
83
|
-
colors: [baseColor, highlightColor, highlightColor, baseColor],
|
|
84
|
-
start: {
|
|
85
|
-
x: 0,
|
|
86
|
-
y: 0
|
|
87
|
-
},
|
|
88
|
-
end: {
|
|
89
|
-
x: 1,
|
|
90
|
-
y: 0
|
|
91
|
-
},
|
|
92
|
-
style: _reactNative.StyleSheet.absoluteFill
|
|
93
|
-
}))), children);
|
|
62
|
+
pointerEvents: "none",
|
|
63
|
+
style: [_reactNative.StyleSheet.absoluteFill, animatedStyle]
|
|
64
|
+
}, /*#__PURE__*/_react.default.createElement(_reactNative.View, {
|
|
65
|
+
style: gradientStyle
|
|
66
|
+
})), children);
|
|
94
67
|
}
|
|
95
68
|
const styles = _reactNative.StyleSheet.create({
|
|
96
69
|
container: {
|
|
97
70
|
overflow: 'hidden'
|
|
98
|
-
},
|
|
99
|
-
shimmer: {
|
|
100
|
-
position: 'absolute'
|
|
101
71
|
}
|
|
102
72
|
});
|
|
103
73
|
//# sourceMappingURL=ShimmerAnimation.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"names":["_react","_interopRequireWildcard","require","_reactNative","_reactNativeReanimated","
|
|
1
|
+
{"version":3,"names":["_react","_interopRequireWildcard","require","_reactNative","_reactNativeReanimated","e","t","WeakMap","r","n","__esModule","o","i","f","__proto__","default","has","get","set","hasOwnProperty","call","Object","defineProperty","getOwnPropertyDescriptor","ShimmerAnimation","width","height","baseColor","highlightColor","duration","borderRadius","style","children","progress","useSharedValue","measuredWidth","setMeasuredWidth","useState","useEffect","value","withRepeat","withTiming","easing","Easing","linear","onLayout","event","w","nativeEvent","layout","prev","containerWidth","animatedStyle","useAnimatedStyle","translateX","interpolate","transform","gradientStyle","StyleSheet","absoluteFillObject","experimental_backgroundImage","createElement","View","styles","container","backgroundColor","pointerEvents","absoluteFill","create","overflow"],"sourceRoot":"../../../src","sources":["animations/ShimmerAnimation.tsx"],"mappings":"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","ignoreList":[]}
|
|
@@ -1,23 +1,7 @@
|
|
|
1
1
|
import React, { useEffect } from 'react';
|
|
2
2
|
import { StyleSheet } from 'react-native';
|
|
3
3
|
import Animated, { useSharedValue, useAnimatedStyle, withRepeat, withSequence, withTiming, Easing } from 'react-native-reanimated';
|
|
4
|
-
|
|
5
|
-
* Pulse animation component using Reanimated 3
|
|
6
|
-
*
|
|
7
|
-
* Creates a subtle breathing/pulsing effect using:
|
|
8
|
-
* - Shared values for performance
|
|
9
|
-
* - Worklets for 60 FPS on UI thread
|
|
10
|
-
* - Opacity animation for pulse effect
|
|
11
|
-
*
|
|
12
|
-
* @example
|
|
13
|
-
* ```tsx
|
|
14
|
-
* <PulseAnimation
|
|
15
|
-
* width={200}
|
|
16
|
-
* height={20}
|
|
17
|
-
* borderRadius={4}
|
|
18
|
-
* />
|
|
19
|
-
* ```
|
|
20
|
-
*/
|
|
4
|
+
// Breathing opacity loop on the UI thread with Reanimated.
|
|
21
5
|
export function PulseAnimation({
|
|
22
6
|
width,
|
|
23
7
|
height,
|
|
@@ -27,24 +11,16 @@ export function PulseAnimation({
|
|
|
27
11
|
style,
|
|
28
12
|
children
|
|
29
13
|
}) {
|
|
30
|
-
// Shared value for opacity (0 to 1)
|
|
31
14
|
const opacity = useSharedValue(1);
|
|
32
15
|
useEffect(() => {
|
|
33
|
-
// Start infinite pulse animation
|
|
34
|
-
// Sequence: fade out -> fade in
|
|
35
16
|
opacity.value = withRepeat(withSequence(withTiming(0.5, {
|
|
36
17
|
duration: duration / 2,
|
|
37
18
|
easing: Easing.inOut(Easing.ease)
|
|
38
19
|
}), withTiming(1, {
|
|
39
20
|
duration: duration / 2,
|
|
40
21
|
easing: Easing.inOut(Easing.ease)
|
|
41
|
-
})), -1,
|
|
42
|
-
// Infinite repeat
|
|
43
|
-
false // Don't reverse
|
|
44
|
-
);
|
|
22
|
+
})), -1, false);
|
|
45
23
|
}, [duration, opacity]);
|
|
46
|
-
|
|
47
|
-
// Animated style for pulse effect
|
|
48
24
|
const animatedStyle = useAnimatedStyle(() => {
|
|
49
25
|
'worklet';
|
|
50
26
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"names":["React","useEffect","StyleSheet","Animated","useSharedValue","useAnimatedStyle","withRepeat","withSequence","withTiming","Easing","PulseAnimation","width","height","baseColor","duration","borderRadius","style","children","opacity","value","easing","inOut","ease","animatedStyle","createElement","View","styles","container","backgroundColor","create","overflow"],"sourceRoot":"../../../src","sources":["animations/PulseAnimation.tsx"],"mappings":"AAAA,OAAOA,KAAK,IAAIC,SAAS,QAAQ,OAAO;AAExC,SAASC,UAAU,QAAQ,cAAc;AACzC,OAAOC,QAAQ,IACbC,cAAc,EACdC,gBAAgB,EAChBC,UAAU,EACVC,YAAY,EACZC,UAAU,EACVC,MAAM,QACD,yBAAyB;
|
|
1
|
+
{"version":3,"names":["React","useEffect","StyleSheet","Animated","useSharedValue","useAnimatedStyle","withRepeat","withSequence","withTiming","Easing","PulseAnimation","width","height","baseColor","duration","borderRadius","style","children","opacity","value","easing","inOut","ease","animatedStyle","createElement","View","styles","container","backgroundColor","create","overflow"],"sourceRoot":"../../../src","sources":["animations/PulseAnimation.tsx"],"mappings":"AAAA,OAAOA,KAAK,IAAIC,SAAS,QAAQ,OAAO;AAExC,SAASC,UAAU,QAAQ,cAAc;AACzC,OAAOC,QAAQ,IACbC,cAAc,EACdC,gBAAgB,EAChBC,UAAU,EACVC,YAAY,EACZC,UAAU,EACVC,MAAM,QACD,yBAAyB;AAahC;AACA,OAAO,SAASC,cAAcA,CAAC;EAC7BC,KAAK;EACLC,MAAM;EACNC,SAAS,GAAG,SAAS;EACrBC,QAAQ,GAAG,IAAI;EACfC,YAAY,GAAG,CAAC;EAChBC,KAAK;EACLC;AACU,CAAC,EAAE;EACb,MAAMC,OAAO,GAAGd,cAAc,CAAC,CAAC,CAAC;EAEjCH,SAAS,CAAC,MAAM;IACdiB,OAAO,CAACC,KAAK,GAAGb,UAAU,CACxBC,YAAY,CACVC,UAAU,CAAC,GAAG,EAAE;MACdM,QAAQ,EAAEA,QAAQ,GAAG,CAAC;MACtBM,MAAM,EAAEX,MAAM,CAACY,KAAK,CAACZ,MAAM,CAACa,IAAI;IAClC,CAAC,CAAC,EACFd,UAAU,CAAC,CAAC,EAAE;MACZM,QAAQ,EAAEA,QAAQ,GAAG,CAAC;MACtBM,MAAM,EAAEX,MAAM,CAACY,KAAK,CAACZ,MAAM,CAACa,IAAI;IAClC,CAAC,CACH,CAAC,EACD,CAAC,CAAC,EACF,KACF,CAAC;EACH,CAAC,EAAE,CAACR,QAAQ,EAAEI,OAAO,CAAC,CAAC;EAEvB,MAAMK,aAAa,GAAGlB,gBAAgB,CAAC,MAAM;IAC3C,SAAS;;IACT,OAAO;MAAEa,OAAO,EAAEA,OAAO,CAACC;IAAM,CAAC;EACnC,CAAC,EAAE,EAAE,CAAC;EAEN,oBACEnB,KAAA,CAAAwB,aAAA,CAACrB,QAAQ,CAACsB,IAAI;IACZT,KAAK,EAAE,CACLU,MAAM,CAACC,SAAS,EAChB;MAAEhB,KAAK;MAAEC,MAAM;MAAEG,YAAY;MAAEa,eAAe,EAAEf;IAAU,CAAC,EAC3DU,aAAa,EACbP,KAAK;EACL,GAEDC,QACY,CAAC;AAEpB;AAEA,MAAMS,MAAM,GAAGxB,UAAU,CAAC2B,MAAM,CAAC;EAC/BF,SAAS,EAAE;IACTG,QAAQ,EAAE;EACZ;AACF,CAAC,CAAC","ignoreList":[]}
|
|
@@ -1,24 +1,9 @@
|
|
|
1
|
-
import React, { useEffect } from 'react';
|
|
1
|
+
import React, { useEffect, useState } from 'react';
|
|
2
2
|
import { StyleSheet, View } from 'react-native';
|
|
3
3
|
import Animated, { useSharedValue, useAnimatedStyle, withRepeat, withTiming, interpolate, Easing } from 'react-native-reanimated';
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
*
|
|
8
|
-
* Creates a smooth left-to-right shimmer effect using:
|
|
9
|
-
* - Shared values for performance
|
|
10
|
-
* - Worklets for 60 FPS on UI thread
|
|
11
|
-
* - Linear gradient for shimmer effect
|
|
12
|
-
*
|
|
13
|
-
* @example
|
|
14
|
-
* ```tsx
|
|
15
|
-
* <ShimmerAnimation
|
|
16
|
-
* width={200}
|
|
17
|
-
* height={20}
|
|
18
|
-
* borderRadius={4}
|
|
19
|
-
* />
|
|
20
|
-
* ```
|
|
21
|
-
*/
|
|
4
|
+
// Shimmer with no gradient library: the sweeping overlay uses React Native's
|
|
5
|
+
// built-in `experimental_backgroundImage` CSS gradient (New Architecture),
|
|
6
|
+
// animated on the UI thread with Reanimated.
|
|
22
7
|
export function ShimmerAnimation({
|
|
23
8
|
width,
|
|
24
9
|
height,
|
|
@@ -29,33 +14,37 @@ export function ShimmerAnimation({
|
|
|
29
14
|
style,
|
|
30
15
|
children
|
|
31
16
|
}) {
|
|
32
|
-
// Shared value for animation progress (0 to 1)
|
|
33
17
|
const progress = useSharedValue(0);
|
|
18
|
+
const [measuredWidth, setMeasuredWidth] = useState(0);
|
|
34
19
|
useEffect(() => {
|
|
35
|
-
// Start infinite shimmer animation
|
|
36
20
|
progress.value = withRepeat(withTiming(1, {
|
|
37
21
|
duration,
|
|
38
22
|
easing: Easing.linear
|
|
39
|
-
}), -1,
|
|
40
|
-
// Infinite repeat
|
|
41
|
-
false // Don't reverse
|
|
42
|
-
);
|
|
23
|
+
}), -1, false);
|
|
43
24
|
}, [duration, progress]);
|
|
44
|
-
|
|
45
|
-
|
|
25
|
+
const onLayout = event => {
|
|
26
|
+
const w = event.nativeEvent.layout.width;
|
|
27
|
+
setMeasuredWidth(prev => prev === w ? prev : w);
|
|
28
|
+
};
|
|
29
|
+
const containerWidth = typeof width === 'number' ? width : measuredWidth;
|
|
46
30
|
const animatedStyle = useAnimatedStyle(() => {
|
|
47
31
|
'worklet';
|
|
48
32
|
|
|
49
|
-
|
|
50
|
-
// Interpolate from -100% to 100% to create left-to-right sweep
|
|
51
|
-
const translateX = interpolate(progress.value, [0, 1], [-1, 1]);
|
|
33
|
+
const translateX = interpolate(progress.value, [0, 1], [-containerWidth, containerWidth]);
|
|
52
34
|
return {
|
|
53
35
|
transform: [{
|
|
54
|
-
translateX
|
|
36
|
+
translateX
|
|
55
37
|
}]
|
|
56
38
|
};
|
|
57
|
-
}, [
|
|
39
|
+
}, [containerWidth]);
|
|
40
|
+
|
|
41
|
+
// `experimental_backgroundImage` isn't in the ViewStyle types yet.
|
|
42
|
+
const gradientStyle = {
|
|
43
|
+
...StyleSheet.absoluteFillObject,
|
|
44
|
+
experimental_backgroundImage: `linear-gradient(to right, ${baseColor}, ${highlightColor}, ${baseColor})`
|
|
45
|
+
};
|
|
58
46
|
return /*#__PURE__*/React.createElement(View, {
|
|
47
|
+
onLayout: onLayout,
|
|
59
48
|
style: [styles.container, {
|
|
60
49
|
width,
|
|
61
50
|
height,
|
|
@@ -63,34 +52,15 @@ export function ShimmerAnimation({
|
|
|
63
52
|
backgroundColor: baseColor
|
|
64
53
|
}, style]
|
|
65
54
|
}, /*#__PURE__*/React.createElement(Animated.View, {
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
},
|
|
71
|
-
style: [styles.shimmer, {
|
|
72
|
-
width: typeof width === 'number' ? width : '100%',
|
|
73
|
-
height: '100%'
|
|
74
|
-
}, animatedStyle]
|
|
75
|
-
}, /*#__PURE__*/React.createElement(LinearGradient, {
|
|
76
|
-
colors: [baseColor, highlightColor, highlightColor, baseColor],
|
|
77
|
-
start: {
|
|
78
|
-
x: 0,
|
|
79
|
-
y: 0
|
|
80
|
-
},
|
|
81
|
-
end: {
|
|
82
|
-
x: 1,
|
|
83
|
-
y: 0
|
|
84
|
-
},
|
|
85
|
-
style: StyleSheet.absoluteFill
|
|
86
|
-
}))), children);
|
|
55
|
+
pointerEvents: "none",
|
|
56
|
+
style: [StyleSheet.absoluteFill, animatedStyle]
|
|
57
|
+
}, /*#__PURE__*/React.createElement(View, {
|
|
58
|
+
style: gradientStyle
|
|
59
|
+
})), children);
|
|
87
60
|
}
|
|
88
61
|
const styles = StyleSheet.create({
|
|
89
62
|
container: {
|
|
90
63
|
overflow: 'hidden'
|
|
91
|
-
},
|
|
92
|
-
shimmer: {
|
|
93
|
-
position: 'absolute'
|
|
94
64
|
}
|
|
95
65
|
});
|
|
96
66
|
//# sourceMappingURL=ShimmerAnimation.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"names":["React","useEffect","StyleSheet","View","Animated","useSharedValue","useAnimatedStyle","withRepeat","withTiming","interpolate","Easing","
|
|
1
|
+
{"version":3,"names":["React","useEffect","useState","StyleSheet","View","Animated","useSharedValue","useAnimatedStyle","withRepeat","withTiming","interpolate","Easing","ShimmerAnimation","width","height","baseColor","highlightColor","duration","borderRadius","style","children","progress","measuredWidth","setMeasuredWidth","value","easing","linear","onLayout","event","w","nativeEvent","layout","prev","containerWidth","animatedStyle","translateX","transform","gradientStyle","absoluteFillObject","experimental_backgroundImage","createElement","styles","container","backgroundColor","pointerEvents","absoluteFill","create","overflow"],"sourceRoot":"../../../src","sources":["animations/ShimmerAnimation.tsx"],"mappings":"AAAA,OAAOA,KAAK,IAAIC,SAAS,EAAEC,QAAQ,QAAQ,OAAO;AAOlD,SAASC,UAAU,EAAEC,IAAI,QAAQ,cAAc;AAC/C,OAAOC,QAAQ,IACbC,cAAc,EACdC,gBAAgB,EAChBC,UAAU,EACVC,UAAU,EACVC,WAAW,EACXC,MAAM,QACD,yBAAyB;AAahC;AACA;AACA;AACA,OAAO,SAASC,gBAAgBA,CAAC;EAC/BC,KAAK;EACLC,MAAM;EACNC,SAAS,GAAG,SAAS;EACrBC,cAAc,GAAG,SAAS;EAC1BC,QAAQ,GAAG,IAAI;EACfC,YAAY,GAAG,CAAC;EAChBC,KAAK;EACLC;AACY,CAAC,EAAE;EACf,MAAMC,QAAQ,GAAGf,cAAc,CAAC,CAAC,CAAC;EAClC,MAAM,CAACgB,aAAa,EAAEC,gBAAgB,CAAC,GAAGrB,QAAQ,CAAC,CAAC,CAAC;EAErDD,SAAS,CAAC,MAAM;IACdoB,QAAQ,CAACG,KAAK,GAAGhB,UAAU,CACzBC,UAAU,CAAC,CAAC,EAAE;MAAEQ,QAAQ;MAAEQ,MAAM,EAAEd,MAAM,CAACe;IAAO,CAAC,CAAC,EAClD,CAAC,CAAC,EACF,KACF,CAAC;EACH,CAAC,EAAE,CAACT,QAAQ,EAAEI,QAAQ,CAAC,CAAC;EAExB,MAAMM,QAAQ,GAAIC,KAAwB,IAAK;IAC7C,MAAMC,CAAC,GAAGD,KAAK,CAACE,WAAW,CAACC,MAAM,CAAClB,KAAK;IACxCU,gBAAgB,CAACS,IAAI,IAAKA,IAAI,KAAKH,CAAC,GAAGG,IAAI,GAAGH,CAAE,CAAC;EACnD,CAAC;EAED,MAAMI,cAAc,GAAG,OAAOpB,KAAK,KAAK,QAAQ,GAAGA,KAAK,GAAGS,aAAa;EAExE,MAAMY,aAAa,GAAG3B,gBAAgB,CAAC,MAAM;IAC3C,SAAS;;IACT,MAAM4B,UAAU,GAAGzB,WAAW,CAC5BW,QAAQ,CAACG,KAAK,EACd,CAAC,CAAC,EAAE,CAAC,CAAC,EACN,CAAC,CAACS,cAAc,EAAEA,cAAc,CAClC,CAAC;IACD,OAAO;MAAEG,SAAS,EAAE,CAAC;QAAED;MAAW,CAAC;IAAE,CAAC;EACxC,CAAC,EAAE,CAACF,cAAc,CAAC,CAAC;;EAEpB;EACA,MAAMI,aAAa,GAAG;IACpB,GAAGlC,UAAU,CAACmC,kBAAkB;IAChCC,4BAA4B,EAAE,6BAA6BxB,SAAS,KAAKC,cAAc,KAAKD,SAAS;EACvG,CAAyB;EAEzB,oBACEf,KAAA,CAAAwC,aAAA,CAACpC,IAAI;IACHuB,QAAQ,EAAEA,QAAS;IACnBR,KAAK,EAAE,CACLsB,MAAM,CAACC,SAAS,EAChB;MAAE7B,KAAK;MAAEC,MAAM;MAAEI,YAAY;MAAEyB,eAAe,EAAE5B;IAAU,CAAC,EAC3DI,KAAK;EACL,gBAEFnB,KAAA,CAAAwC,aAAA,CAACnC,QAAQ,CAACD,IAAI;IACZwC,aAAa,EAAC,MAAM;IACpBzB,KAAK,EAAE,CAAChB,UAAU,CAAC0C,YAAY,EAAEX,aAAa;EAAE,gBAEhDlC,KAAA,CAAAwC,aAAA,CAACpC,IAAI;IAACe,KAAK,EAAEkB;EAAc,CAAE,CAChB,CAAC,EAEfjB,QACG,CAAC;AAEX;AAEA,MAAMqB,MAAM,GAAGtC,UAAU,CAAC2C,MAAM,CAAC;EAC/BJ,SAAS,EAAE;IACTK,QAAQ,EAAE;EACZ;AACF,CAAC,CAAC","ignoreList":[]}
|
|
@@ -1,59 +1,15 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import type { DimensionValue, StyleProp, ViewStyle } from 'react-native';
|
|
3
3
|
interface PulseProps {
|
|
4
|
-
/**
|
|
5
|
-
* Width of the pulse container
|
|
6
|
-
*/
|
|
7
4
|
width?: DimensionValue;
|
|
8
|
-
/**
|
|
9
|
-
* Height of the pulse container
|
|
10
|
-
*/
|
|
11
5
|
height?: DimensionValue;
|
|
12
|
-
/**
|
|
13
|
-
* Base color of the skeleton
|
|
14
|
-
* @default '#E1E9EE'
|
|
15
|
-
*/
|
|
16
6
|
baseColor?: string;
|
|
17
|
-
/**
|
|
18
|
-
* Highlight color for pulse
|
|
19
|
-
* @default '#F2F8FC'
|
|
20
|
-
*/
|
|
21
7
|
highlightColor?: string;
|
|
22
|
-
/**
|
|
23
|
-
* Animation duration in milliseconds
|
|
24
|
-
* @default 1000
|
|
25
|
-
*/
|
|
26
8
|
duration?: number;
|
|
27
|
-
/**
|
|
28
|
-
* Border radius
|
|
29
|
-
*/
|
|
30
9
|
borderRadius?: number;
|
|
31
|
-
/**
|
|
32
|
-
* Additional styles
|
|
33
|
-
*/
|
|
34
10
|
style?: StyleProp<ViewStyle>;
|
|
35
|
-
/**
|
|
36
|
-
* Child content (rendered inside pulse)
|
|
37
|
-
*/
|
|
38
11
|
children?: React.ReactNode;
|
|
39
12
|
}
|
|
40
|
-
/**
|
|
41
|
-
* Pulse animation component using Reanimated 3
|
|
42
|
-
*
|
|
43
|
-
* Creates a subtle breathing/pulsing effect using:
|
|
44
|
-
* - Shared values for performance
|
|
45
|
-
* - Worklets for 60 FPS on UI thread
|
|
46
|
-
* - Opacity animation for pulse effect
|
|
47
|
-
*
|
|
48
|
-
* @example
|
|
49
|
-
* ```tsx
|
|
50
|
-
* <PulseAnimation
|
|
51
|
-
* width={200}
|
|
52
|
-
* height={20}
|
|
53
|
-
* borderRadius={4}
|
|
54
|
-
* />
|
|
55
|
-
* ```
|
|
56
|
-
*/
|
|
57
13
|
export declare function PulseAnimation({ width, height, baseColor, duration, borderRadius, style, children, }: PulseProps): React.JSX.Element;
|
|
58
14
|
export {};
|
|
59
15
|
//# sourceMappingURL=PulseAnimation.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PulseAnimation.d.ts","sourceRoot":"","sources":["../../../src/animations/PulseAnimation.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAoB,MAAM,OAAO,CAAC;AACzC,OAAO,KAAK,EAAE,cAAc,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAWzE,UAAU,UAAU;IAClB
|
|
1
|
+
{"version":3,"file":"PulseAnimation.d.ts","sourceRoot":"","sources":["../../../src/animations/PulseAnimation.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAoB,MAAM,OAAO,CAAC;AACzC,OAAO,KAAK,EAAE,cAAc,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AAWzE,UAAU,UAAU;IAClB,KAAK,CAAC,EAAE,cAAc,CAAC;IACvB,MAAM,CAAC,EAAE,cAAc,CAAC;IACxB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,KAAK,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IAC7B,QAAQ,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;CAC5B;AAGD,wBAAgB,cAAc,CAAC,EAC7B,KAAK,EACL,MAAM,EACN,SAAqB,EACrB,QAAe,EACf,YAAgB,EAChB,KAAK,EACL,QAAQ,GACT,EAAE,UAAU,qBAqCZ"}
|
|
@@ -1,59 +1,15 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import type { DimensionValue, StyleProp, ViewStyle } from 'react-native';
|
|
3
3
|
interface ShimmerProps {
|
|
4
|
-
/**
|
|
5
|
-
* Width of the shimmer container
|
|
6
|
-
*/
|
|
7
4
|
width?: DimensionValue;
|
|
8
|
-
/**
|
|
9
|
-
* Height of the shimmer container
|
|
10
|
-
*/
|
|
11
5
|
height?: DimensionValue;
|
|
12
|
-
/**
|
|
13
|
-
* Base color of the skeleton
|
|
14
|
-
* @default '#E1E9EE'
|
|
15
|
-
*/
|
|
16
6
|
baseColor?: string;
|
|
17
|
-
/**
|
|
18
|
-
* Highlight color that shimmers across
|
|
19
|
-
* @default '#F2F8FC'
|
|
20
|
-
*/
|
|
21
7
|
highlightColor?: string;
|
|
22
|
-
/**
|
|
23
|
-
* Animation duration in milliseconds
|
|
24
|
-
* @default 1500
|
|
25
|
-
*/
|
|
26
8
|
duration?: number;
|
|
27
|
-
/**
|
|
28
|
-
* Border radius
|
|
29
|
-
*/
|
|
30
9
|
borderRadius?: number;
|
|
31
|
-
/**
|
|
32
|
-
* Additional styles
|
|
33
|
-
*/
|
|
34
10
|
style?: StyleProp<ViewStyle>;
|
|
35
|
-
/**
|
|
36
|
-
* Child content (rendered on top of shimmer)
|
|
37
|
-
*/
|
|
38
11
|
children?: React.ReactNode;
|
|
39
12
|
}
|
|
40
|
-
/**
|
|
41
|
-
* Shimmer animation component using Reanimated 3
|
|
42
|
-
*
|
|
43
|
-
* Creates a smooth left-to-right shimmer effect using:
|
|
44
|
-
* - Shared values for performance
|
|
45
|
-
* - Worklets for 60 FPS on UI thread
|
|
46
|
-
* - Linear gradient for shimmer effect
|
|
47
|
-
*
|
|
48
|
-
* @example
|
|
49
|
-
* ```tsx
|
|
50
|
-
* <ShimmerAnimation
|
|
51
|
-
* width={200}
|
|
52
|
-
* height={20}
|
|
53
|
-
* borderRadius={4}
|
|
54
|
-
* />
|
|
55
|
-
* ```
|
|
56
|
-
*/
|
|
57
13
|
export declare function ShimmerAnimation({ width, height, baseColor, highlightColor, duration, borderRadius, style, children, }: ShimmerProps): React.JSX.Element;
|
|
58
14
|
export {};
|
|
59
15
|
//# sourceMappingURL=ShimmerAnimation.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ShimmerAnimation.d.ts","sourceRoot":"","sources":["../../../src/animations/ShimmerAnimation.tsx"],"names":[],"mappings":"AAAA,OAAO,
|
|
1
|
+
{"version":3,"file":"ShimmerAnimation.d.ts","sourceRoot":"","sources":["../../../src/animations/ShimmerAnimation.tsx"],"names":[],"mappings":"AAAA,OAAO,KAA8B,MAAM,OAAO,CAAC;AACnD,OAAO,KAAK,EACV,cAAc,EAEd,SAAS,EACT,SAAS,EACV,MAAM,cAAc,CAAC;AAWtB,UAAU,YAAY;IACpB,KAAK,CAAC,EAAE,cAAc,CAAC;IACvB,MAAM,CAAC,EAAE,cAAc,CAAC;IACxB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,KAAK,CAAC,EAAE,SAAS,CAAC,SAAS,CAAC,CAAC;IAC7B,QAAQ,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;CAC5B;AAKD,wBAAgB,gBAAgB,CAAC,EAC/B,KAAK,EACL,MAAM,EACN,SAAqB,EACrB,cAA0B,EAC1B,QAAe,EACf,YAAgB,EAChB,KAAK,EACL,QAAQ,GACT,EAAE,YAAY,qBAsDd"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "react-native-skelo",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.3",
|
|
4
4
|
"description": "Automatic skeleton loading for React Native. Write your UI once, Skelo builds the loading state automatically.",
|
|
5
5
|
"main": "lib/commonjs/index.js",
|
|
6
6
|
"module": "lib/module/index.js",
|
|
@@ -88,7 +88,6 @@
|
|
|
88
88
|
"peerDependencies": {
|
|
89
89
|
"react": "*",
|
|
90
90
|
"react-native": "*",
|
|
91
|
-
"react-native-linear-gradient": ">=2.8.0",
|
|
92
91
|
"react-native-reanimated": ">=3.0.0",
|
|
93
92
|
"react-reconciler": ">=0.29.0"
|
|
94
93
|
},
|
|
@@ -113,7 +112,6 @@
|
|
|
113
112
|
"react": "18.2.0",
|
|
114
113
|
"react-native": "0.73.1",
|
|
115
114
|
"react-native-builder-bob": "^0.23.2",
|
|
116
|
-
"react-native-linear-gradient": "^2.8.3",
|
|
117
115
|
"react-native-reanimated": "^3.6.1",
|
|
118
116
|
"react-reconciler": "^0.29.2",
|
|
119
117
|
"react-test-renderer": "18.2.0",
|
|
@@ -129,7 +127,7 @@
|
|
|
129
127
|
"<rootDir>/lib/"
|
|
130
128
|
],
|
|
131
129
|
"transformIgnorePatterns": [
|
|
132
|
-
"node_modules/(?!(@react-native|react-native|react-native-reanimated
|
|
130
|
+
"node_modules/(?!(@react-native|react-native|react-native-reanimated)/)"
|
|
133
131
|
],
|
|
134
132
|
"collectCoverageFrom": [
|
|
135
133
|
"src/**/*.{ts,tsx}",
|
|
@@ -11,67 +11,17 @@ import Animated, {
|
|
|
11
11
|
} from 'react-native-reanimated';
|
|
12
12
|
|
|
13
13
|
interface PulseProps {
|
|
14
|
-
/**
|
|
15
|
-
* Width of the pulse container
|
|
16
|
-
*/
|
|
17
14
|
width?: DimensionValue;
|
|
18
|
-
|
|
19
|
-
/**
|
|
20
|
-
* Height of the pulse container
|
|
21
|
-
*/
|
|
22
15
|
height?: DimensionValue;
|
|
23
|
-
|
|
24
|
-
/**
|
|
25
|
-
* Base color of the skeleton
|
|
26
|
-
* @default '#E1E9EE'
|
|
27
|
-
*/
|
|
28
16
|
baseColor?: string;
|
|
29
|
-
|
|
30
|
-
/**
|
|
31
|
-
* Highlight color for pulse
|
|
32
|
-
* @default '#F2F8FC'
|
|
33
|
-
*/
|
|
34
17
|
highlightColor?: string;
|
|
35
|
-
|
|
36
|
-
/**
|
|
37
|
-
* Animation duration in milliseconds
|
|
38
|
-
* @default 1000
|
|
39
|
-
*/
|
|
40
18
|
duration?: number;
|
|
41
|
-
|
|
42
|
-
/**
|
|
43
|
-
* Border radius
|
|
44
|
-
*/
|
|
45
19
|
borderRadius?: number;
|
|
46
|
-
|
|
47
|
-
/**
|
|
48
|
-
* Additional styles
|
|
49
|
-
*/
|
|
50
20
|
style?: StyleProp<ViewStyle>;
|
|
51
|
-
|
|
52
|
-
/**
|
|
53
|
-
* Child content (rendered inside pulse)
|
|
54
|
-
*/
|
|
55
21
|
children?: React.ReactNode;
|
|
56
22
|
}
|
|
57
23
|
|
|
58
|
-
|
|
59
|
-
* Pulse animation component using Reanimated 3
|
|
60
|
-
*
|
|
61
|
-
* Creates a subtle breathing/pulsing effect using:
|
|
62
|
-
* - Shared values for performance
|
|
63
|
-
* - Worklets for 60 FPS on UI thread
|
|
64
|
-
* - Opacity animation for pulse effect
|
|
65
|
-
*
|
|
66
|
-
* @example
|
|
67
|
-
* ```tsx
|
|
68
|
-
* <PulseAnimation
|
|
69
|
-
* width={200}
|
|
70
|
-
* height={20}
|
|
71
|
-
* borderRadius={4}
|
|
72
|
-
* />
|
|
73
|
-
* ```
|
|
74
|
-
*/
|
|
24
|
+
// Breathing opacity loop on the UI thread with Reanimated.
|
|
75
25
|
export function PulseAnimation({
|
|
76
26
|
width,
|
|
77
27
|
height,
|
|
@@ -81,12 +31,9 @@ export function PulseAnimation({
|
|
|
81
31
|
style,
|
|
82
32
|
children,
|
|
83
33
|
}: PulseProps) {
|
|
84
|
-
// Shared value for opacity (0 to 1)
|
|
85
34
|
const opacity = useSharedValue(1);
|
|
86
35
|
|
|
87
36
|
useEffect(() => {
|
|
88
|
-
// Start infinite pulse animation
|
|
89
|
-
// Sequence: fade out -> fade in
|
|
90
37
|
opacity.value = withRepeat(
|
|
91
38
|
withSequence(
|
|
92
39
|
withTiming(0.5, {
|
|
@@ -98,30 +45,21 @@ export function PulseAnimation({
|
|
|
98
45
|
easing: Easing.inOut(Easing.ease),
|
|
99
46
|
})
|
|
100
47
|
),
|
|
101
|
-
-1,
|
|
102
|
-
false
|
|
48
|
+
-1,
|
|
49
|
+
false
|
|
103
50
|
);
|
|
104
51
|
}, [duration, opacity]);
|
|
105
52
|
|
|
106
|
-
// Animated style for pulse effect
|
|
107
53
|
const animatedStyle = useAnimatedStyle(() => {
|
|
108
54
|
'worklet';
|
|
109
|
-
|
|
110
|
-
return {
|
|
111
|
-
opacity: opacity.value,
|
|
112
|
-
};
|
|
55
|
+
return { opacity: opacity.value };
|
|
113
56
|
}, []);
|
|
114
57
|
|
|
115
58
|
return (
|
|
116
59
|
<Animated.View
|
|
117
60
|
style={[
|
|
118
61
|
styles.container,
|
|
119
|
-
{
|
|
120
|
-
width,
|
|
121
|
-
height,
|
|
122
|
-
borderRadius,
|
|
123
|
-
backgroundColor: baseColor,
|
|
124
|
-
},
|
|
62
|
+
{ width, height, borderRadius, backgroundColor: baseColor },
|
|
125
63
|
animatedStyle,
|
|
126
64
|
style,
|
|
127
65
|
]}
|
|
@@ -1,5 +1,10 @@
|
|
|
1
|
-
import React, { useEffect } from 'react';
|
|
2
|
-
import type {
|
|
1
|
+
import React, { useEffect, useState } from 'react';
|
|
2
|
+
import type {
|
|
3
|
+
DimensionValue,
|
|
4
|
+
LayoutChangeEvent,
|
|
5
|
+
StyleProp,
|
|
6
|
+
ViewStyle,
|
|
7
|
+
} from 'react-native';
|
|
3
8
|
import { StyleSheet, View } from 'react-native';
|
|
4
9
|
import Animated, {
|
|
5
10
|
useSharedValue,
|
|
@@ -9,70 +14,21 @@ import Animated, {
|
|
|
9
14
|
interpolate,
|
|
10
15
|
Easing,
|
|
11
16
|
} from 'react-native-reanimated';
|
|
12
|
-
import { LinearGradient } from 'react-native-linear-gradient';
|
|
13
17
|
|
|
14
18
|
interface ShimmerProps {
|
|
15
|
-
/**
|
|
16
|
-
* Width of the shimmer container
|
|
17
|
-
*/
|
|
18
19
|
width?: DimensionValue;
|
|
19
|
-
|
|
20
|
-
/**
|
|
21
|
-
* Height of the shimmer container
|
|
22
|
-
*/
|
|
23
20
|
height?: DimensionValue;
|
|
24
|
-
|
|
25
|
-
/**
|
|
26
|
-
* Base color of the skeleton
|
|
27
|
-
* @default '#E1E9EE'
|
|
28
|
-
*/
|
|
29
21
|
baseColor?: string;
|
|
30
|
-
|
|
31
|
-
/**
|
|
32
|
-
* Highlight color that shimmers across
|
|
33
|
-
* @default '#F2F8FC'
|
|
34
|
-
*/
|
|
35
22
|
highlightColor?: string;
|
|
36
|
-
|
|
37
|
-
/**
|
|
38
|
-
* Animation duration in milliseconds
|
|
39
|
-
* @default 1500
|
|
40
|
-
*/
|
|
41
23
|
duration?: number;
|
|
42
|
-
|
|
43
|
-
/**
|
|
44
|
-
* Border radius
|
|
45
|
-
*/
|
|
46
24
|
borderRadius?: number;
|
|
47
|
-
|
|
48
|
-
/**
|
|
49
|
-
* Additional styles
|
|
50
|
-
*/
|
|
51
25
|
style?: StyleProp<ViewStyle>;
|
|
52
|
-
|
|
53
|
-
/**
|
|
54
|
-
* Child content (rendered on top of shimmer)
|
|
55
|
-
*/
|
|
56
26
|
children?: React.ReactNode;
|
|
57
27
|
}
|
|
58
28
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
* Creates a smooth left-to-right shimmer effect using:
|
|
63
|
-
* - Shared values for performance
|
|
64
|
-
* - Worklets for 60 FPS on UI thread
|
|
65
|
-
* - Linear gradient for shimmer effect
|
|
66
|
-
*
|
|
67
|
-
* @example
|
|
68
|
-
* ```tsx
|
|
69
|
-
* <ShimmerAnimation
|
|
70
|
-
* width={200}
|
|
71
|
-
* height={20}
|
|
72
|
-
* borderRadius={4}
|
|
73
|
-
* />
|
|
74
|
-
* ```
|
|
75
|
-
*/
|
|
29
|
+
// Shimmer with no gradient library: the sweeping overlay uses React Native's
|
|
30
|
+
// built-in `experimental_backgroundImage` CSS gradient (New Architecture),
|
|
31
|
+
// animated on the UI thread with Reanimated.
|
|
76
32
|
export function ShimmerAnimation({
|
|
77
33
|
width,
|
|
78
34
|
height,
|
|
@@ -83,77 +39,56 @@ export function ShimmerAnimation({
|
|
|
83
39
|
style,
|
|
84
40
|
children,
|
|
85
41
|
}: ShimmerProps) {
|
|
86
|
-
// Shared value for animation progress (0 to 1)
|
|
87
42
|
const progress = useSharedValue(0);
|
|
43
|
+
const [measuredWidth, setMeasuredWidth] = useState(0);
|
|
88
44
|
|
|
89
45
|
useEffect(() => {
|
|
90
|
-
// Start infinite shimmer animation
|
|
91
46
|
progress.value = withRepeat(
|
|
92
|
-
withTiming(1, {
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
}),
|
|
96
|
-
-1, // Infinite repeat
|
|
97
|
-
false // Don't reverse
|
|
47
|
+
withTiming(1, { duration, easing: Easing.linear }),
|
|
48
|
+
-1,
|
|
49
|
+
false
|
|
98
50
|
);
|
|
99
51
|
}, [duration, progress]);
|
|
100
52
|
|
|
101
|
-
|
|
53
|
+
const onLayout = (event: LayoutChangeEvent) => {
|
|
54
|
+
const w = event.nativeEvent.layout.width;
|
|
55
|
+
setMeasuredWidth(prev => (prev === w ? prev : w));
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
const containerWidth = typeof width === 'number' ? width : measuredWidth;
|
|
59
|
+
|
|
102
60
|
const animatedStyle = useAnimatedStyle(() => {
|
|
103
61
|
'worklet';
|
|
62
|
+
const translateX = interpolate(
|
|
63
|
+
progress.value,
|
|
64
|
+
[0, 1],
|
|
65
|
+
[-containerWidth, containerWidth]
|
|
66
|
+
);
|
|
67
|
+
return { transform: [{ translateX }] };
|
|
68
|
+
}, [containerWidth]);
|
|
104
69
|
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
transform: [{ translateX: translateX * (typeof width === 'number' ? width : 200) }],
|
|
111
|
-
};
|
|
112
|
-
}, [width]);
|
|
70
|
+
// `experimental_backgroundImage` isn't in the ViewStyle types yet.
|
|
71
|
+
const gradientStyle = {
|
|
72
|
+
...StyleSheet.absoluteFillObject,
|
|
73
|
+
experimental_backgroundImage: `linear-gradient(to right, ${baseColor}, ${highlightColor}, ${baseColor})`,
|
|
74
|
+
} as unknown as ViewStyle;
|
|
113
75
|
|
|
114
76
|
return (
|
|
115
77
|
<View
|
|
78
|
+
onLayout={onLayout}
|
|
116
79
|
style={[
|
|
117
80
|
styles.container,
|
|
118
|
-
{
|
|
119
|
-
width,
|
|
120
|
-
height,
|
|
121
|
-
borderRadius,
|
|
122
|
-
backgroundColor: baseColor,
|
|
123
|
-
},
|
|
81
|
+
{ width, height, borderRadius, backgroundColor: baseColor },
|
|
124
82
|
style,
|
|
125
83
|
]}
|
|
126
84
|
>
|
|
127
|
-
{/* Shimmer gradient overlay */}
|
|
128
85
|
<Animated.View
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
{
|
|
132
|
-
overflow: 'hidden',
|
|
133
|
-
borderRadius,
|
|
134
|
-
},
|
|
135
|
-
]}
|
|
86
|
+
pointerEvents="none"
|
|
87
|
+
style={[StyleSheet.absoluteFill, animatedStyle]}
|
|
136
88
|
>
|
|
137
|
-
<
|
|
138
|
-
style={[
|
|
139
|
-
styles.shimmer,
|
|
140
|
-
{
|
|
141
|
-
width: typeof width === 'number' ? width : '100%',
|
|
142
|
-
height: '100%',
|
|
143
|
-
},
|
|
144
|
-
animatedStyle,
|
|
145
|
-
]}
|
|
146
|
-
>
|
|
147
|
-
<LinearGradient
|
|
148
|
-
colors={[baseColor, highlightColor, highlightColor, baseColor]}
|
|
149
|
-
start={{ x: 0, y: 0 }}
|
|
150
|
-
end={{ x: 1, y: 0 }}
|
|
151
|
-
style={StyleSheet.absoluteFill}
|
|
152
|
-
/>
|
|
153
|
-
</Animated.View>
|
|
89
|
+
<View style={gradientStyle} />
|
|
154
90
|
</Animated.View>
|
|
155
91
|
|
|
156
|
-
{/* Child content */}
|
|
157
92
|
{children}
|
|
158
93
|
</View>
|
|
159
94
|
);
|
|
@@ -163,7 +98,4 @@ const styles = StyleSheet.create({
|
|
|
163
98
|
container: {
|
|
164
99
|
overflow: 'hidden',
|
|
165
100
|
},
|
|
166
|
-
shimmer: {
|
|
167
|
-
position: 'absolute',
|
|
168
|
-
},
|
|
169
101
|
});
|