Skip to content

Commit e4a5bac

Browse files
feat: add shimmer style object
2 parents 1504526 + b8bf977 commit e4a5bac

File tree

5 files changed

+158
-4
lines changed

5 files changed

+158
-4
lines changed

README.md

Lines changed: 48 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
<img width="1100" alt="header" src="https://github.com/user-attachments/assets/cbf6ecfa-8a0f-4841-8fc0-982aa04e618e" />
22

3-
43
Ready-to-use CSS Animation presets for [React Native Reanimated](https://docs.swmansion.com/react-native-reanimated/)
54

65
> [!TIP]
@@ -75,12 +74,36 @@ function App() {
7574
}
7675
```
7776

77+
### Shimmer
78+
79+
Add `shimmer` style object to an `Animated` component to make it animate from left to right indefinitely. Great for shimmer loading effect.
80+
81+
<img src="https://github.com/user-attachments/assets/81e75ed0-b7ec-4f56-a06a-c593a626cb39" alt="Shimmer animation demo" align="right" width="275" />
82+
83+
> [!NOTE]
84+
> While the `shimmer` style object supports both iOS, Android, and the Web, the example video on the right uses `@react-native-masked-view/masked-view` and `expo-linear-gradient`, and thus doesn't work on the Web.
85+
86+
```jsx
87+
import { shimmer } from 'react-native-css-animations';
88+
import Animated from 'react-native-reanimated';
89+
90+
function App() {
91+
return <Animated.View style={[styles.gradient, shimmer]} />;
92+
}
93+
```
94+
7895
## Alternative API
7996

8097
The following animations are also available in a form of React Native components.
8198

8299
```jsx
83-
import { Spin, Ping, Pulse, Bounce } from 'react-native-css-animations';
100+
import {
101+
Spin,
102+
Ping,
103+
Pulse,
104+
Bounce,
105+
Shimmer,
106+
} from 'react-native-css-animations';
84107

85108
function App() {
86109
return (
@@ -91,6 +114,29 @@ function App() {
91114
}
92115
```
93116

117+
## Customizing animation presets
118+
119+
You can customize the animation style objects by overriding the styles like so:
120+
121+
```diff
122+
import { shimmer } from 'react-native-css-animations';
123+
import Animated from 'react-native-reanimated';
124+
125+
function App() {
126+
return <Animated.View
127+
style={[
128+
styles.gradient,
129+
shimmer,
130+
+ {
131+
+ animationName: {
132+
+ to: { transform: [{ translateX: '100%' }] },
133+
+ },
134+
+ },
135+
]}
136+
>
137+
}
138+
```
139+
94140
## Credits
95141

96142
- The examples and animations were adopted from [Tailwind CSS](https://tailwindcss.com/docs/animation).

example/package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,9 @@
1010
},
1111
"dependencies": {
1212
"@expo/metro-runtime": "~4.0.0",
13+
"@react-native-masked-view/masked-view": "0.3.2",
1314
"expo": "~52.0.25",
15+
"expo-linear-gradient": "~14.0.2",
1416
"expo-status-bar": "~2.0.1",
1517
"react": "18.3.1",
1618
"react-dom": "18.3.1",

example/src/App.tsx

Lines changed: 59 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,18 @@
1-
import { SafeAreaView, StyleSheet, Text, View } from 'react-native';
1+
import {
2+
bounce,
3+
ping,
4+
pulse,
5+
shimmer,
6+
spin,
7+
} from 'react-native-css-animations';
8+
9+
import { Platform, SafeAreaView, StyleSheet, Text, View } from 'react-native';
10+
import { LinearGradient } from 'expo-linear-gradient';
211
import Animated from 'react-native-reanimated';
312
import Fontisto from '@expo/vector-icons/Fontisto';
413
import Entypo from '@expo/vector-icons/Entypo';
514
import EvilIcons from '@expo/vector-icons/EvilIcons';
6-
import { bounce, ping, pulse, spin } from 'react-native-css-animations';
15+
import MaskedView from '@react-native-masked-view/masked-view';
716

817
export default function App() {
918
return (
@@ -32,9 +41,38 @@ export default function App() {
3241
</View>
3342

3443
<Text style={styles.label}>Bounce</Text>
44+
{/* Bounce animation ⬇️ */}
3545
<Animated.View style={[styles.arrow, bounce]}>
3646
<Entypo name="chevron-down" size={24} color="black" />
3747
</Animated.View>
48+
49+
{(Platform.OS === 'ios' || Platform.OS === 'android') && (
50+
<>
51+
<Text style={styles.label}>Shimmer</Text>
52+
<View style={styles.shimmerContainer}>
53+
<MaskedView
54+
style={styles.mask}
55+
maskElement={
56+
<View style={styles.skeletonContainer}>
57+
<Animated.View style={styles.skeletonAvatar} />
58+
<Animated.View style={styles.skeletonText} />
59+
</View>
60+
}
61+
>
62+
{/* Shimmer animation ⬇️ */}
63+
<Animated.View style={[styles.gradientContainer, shimmer]}>
64+
<LinearGradient
65+
colors={['#e2e8f0', '#f8fafc', '#e2e8f0']}
66+
locations={[0.46, 0.5, 0.54]}
67+
start={{ x: 0, y: -5 }}
68+
end={{ x: 1, y: 5 }}
69+
style={styles.gradient}
70+
/>
71+
</Animated.View>
72+
</MaskedView>
73+
</View>
74+
</>
75+
)}
3876
</SafeAreaView>
3977
);
4078
}
@@ -116,4 +154,23 @@ const styles = StyleSheet.create({
116154
justifyContent: 'center',
117155
borderColor: '#e2e8f0',
118156
},
157+
shimmerContainer: {
158+
width: '100%',
159+
height: 48,
160+
alignItems: 'center',
161+
justifyContent: 'center',
162+
},
163+
mask: {
164+
height: 48,
165+
width: 210,
166+
},
167+
gradientContainer: {
168+
flex: 1,
169+
width: '300%',
170+
marginHorizontal: '-100%',
171+
},
172+
gradient: {
173+
flex: 1,
174+
width: '100%',
175+
},
119176
});

src/index.tsx

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,3 +123,29 @@ export function Bounce({
123123
</Animated.View>
124124
);
125125
}
126+
127+
export const shimmer: CSSStyleDeclaration = {
128+
animationName: {
129+
from: {
130+
transform: [{ translateX: '-25%' }],
131+
},
132+
to: {
133+
transform: [{ translateX: '25%' }],
134+
},
135+
},
136+
animationDuration: '1s',
137+
animationIterationCount: 'infinite',
138+
animationTimingFunction: 'linear',
139+
};
140+
141+
export function Shimmer({
142+
style,
143+
children,
144+
...rest
145+
}: React.PropsWithChildren<CSSAnimationProps>): JSX.Element {
146+
return (
147+
<Animated.View style={[shimmer, style]} {...rest}>
148+
{children}
149+
</Animated.View>
150+
);
151+
}

yarn.lock

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2906,6 +2906,16 @@ __metadata:
29062906
languageName: node
29072907
linkType: hard
29082908

2909+
"@react-native-masked-view/masked-view@npm:0.3.2":
2910+
version: 0.3.2
2911+
resolution: "@react-native-masked-view/masked-view@npm:0.3.2"
2912+
peerDependencies:
2913+
react: ">=16"
2914+
react-native: ">=0.57"
2915+
checksum: e35ab882148df3f9b71f04355d2fb1b24d6f2aaf29043f80758f398bdf905eed67734b36b072fa8b934923ff4e3d80ccb5e37d8376cb1825272078b96a21dadc
2916+
languageName: node
2917+
linkType: hard
2918+
29092919
"@react-native/assets-registry@npm:0.76.6":
29102920
version: 0.76.6
29112921
resolution: "@react-native/assets-registry@npm:0.76.6"
@@ -6546,6 +6556,17 @@ __metadata:
65466556
languageName: node
65476557
linkType: hard
65486558

6559+
"expo-linear-gradient@npm:~14.0.2":
6560+
version: 14.0.2
6561+
resolution: "expo-linear-gradient@npm:14.0.2"
6562+
peerDependencies:
6563+
expo: "*"
6564+
react: "*"
6565+
react-native: "*"
6566+
checksum: 3f318f50fae44b1f2f90becf421a1c7c0c2822e0a381031fa6d893899173b7ecc6f0c4eddde699e6825fd091d5576cf5960ba81046aeaf0300f44de1147bb543
6567+
languageName: node
6568+
linkType: hard
6569+
65496570
"expo-modules-autolinking@npm:2.0.5":
65506571
version: 2.0.5
65516572
resolution: "expo-modules-autolinking@npm:2.0.5"
@@ -11785,7 +11806,9 @@ __metadata:
1178511806
dependencies:
1178611807
"@babel/core": ^7.20.0
1178711808
"@expo/metro-runtime": ~4.0.0
11809+
"@react-native-masked-view/masked-view": 0.3.2
1178811810
expo: ~52.0.25
11811+
expo-linear-gradient: ~14.0.2
1178911812
expo-status-bar: ~2.0.1
1179011813
react: 18.3.1
1179111814
react-dom: 18.3.1

0 commit comments

Comments
 (0)