Skip to content

Commit d316120

Browse files
feat: finish off shimmer
1 parent cc2fc29 commit d316120

File tree

3 files changed

+123
-48
lines changed

3 files changed

+123
-48
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/src/App.tsx

Lines changed: 49 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,12 @@
1-
import { bounce, ping, pulse, spin } from 'react-native-css-animations';
1+
import {
2+
bounce,
3+
ping,
4+
pulse,
5+
shimmer,
6+
spin,
7+
} from 'react-native-css-animations';
28

3-
import { SafeAreaView, StyleSheet, Text, View } from 'react-native';
9+
import { Platform, SafeAreaView, StyleSheet, Text, View } from 'react-native';
410
import { LinearGradient } from 'expo-linear-gradient';
511
import Animated from 'react-native-reanimated';
612
import Fontisto from '@expo/vector-icons/Fontisto';
@@ -10,15 +16,6 @@ import MaskedView from '@react-native-masked-view/masked-view';
1016

1117
const AnimatedLinearGradient = Animated.createAnimatedComponent(LinearGradient);
1218

13-
const shimmer = {
14-
from: {
15-
transform: [{ translateX: '-25%' }],
16-
},
17-
to: {
18-
transform: [{ translateX: '25%' }],
19-
},
20-
};
21-
2219
export default function App() {
2320
return (
2421
<SafeAreaView style={styles.container}>
@@ -46,45 +43,38 @@ export default function App() {
4643
</View>
4744

4845
<Text style={styles.label}>Bounce</Text>
46+
{/* Bounce animation ⬇️ */}
4947
<Animated.View style={[styles.arrow, bounce]}>
5048
<Entypo name="chevron-down" size={24} color="black" />
5149
</Animated.View>
5250

53-
<Text style={styles.label}>Skimmer</Text>
54-
<View style={styles.shimmerContainer}>
55-
<MaskedView
56-
style={{ height: 48, width: 48 + 12 + 150 }}
57-
maskElement={
58-
<View style={styles.skeletonContainer}>
59-
<Animated.View style={styles.skeletonAvatar} />
60-
<Animated.View style={styles.skeletonText} />
61-
</View>
62-
}
63-
>
64-
<Animated.View
65-
style={{
66-
flex: 1,
67-
width: '300%',
68-
marginHorizontal: '-100%',
69-
animationName: shimmer,
70-
animationDuration: '1s',
71-
animationIterationCount: 'infinite',
72-
animationTimingFunction: 'linear',
73-
}}
74-
>
75-
<AnimatedLinearGradient
76-
colors={['#e2e8f0', '#f8fafc', '#e2e8f0']}
77-
locations={[0.46, 0.5, 0.54]}
78-
start={{ x: 0, y: -5 }}
79-
end={{ x: 1, y: 5 }}
80-
style={{
81-
flex: 1,
82-
width: '100%',
83-
}}
84-
/>
85-
</Animated.View>
86-
</MaskedView>
87-
</View>
51+
{(Platform.OS === 'ios' || Platform.OS === 'android') && (
52+
<>
53+
<Text style={styles.label}>Shimmer</Text>
54+
<View style={styles.shimmerContainer}>
55+
<MaskedView
56+
style={styles.mask}
57+
maskElement={
58+
<View style={styles.skeletonContainer}>
59+
<Animated.View style={styles.skeletonAvatar} />
60+
<Animated.View style={styles.skeletonText} />
61+
</View>
62+
}
63+
>
64+
{/* Shimmer animation ⬇️ */}
65+
<Animated.View style={[styles.gradientContainer, shimmer]}>
66+
<AnimatedLinearGradient
67+
colors={['#e2e8f0', '#f8fafc', '#e2e8f0']}
68+
locations={[0.46, 0.5, 0.54]}
69+
start={{ x: 0, y: -5 }}
70+
end={{ x: 1, y: 5 }}
71+
style={styles.gradient}
72+
/>
73+
</Animated.View>
74+
</MaskedView>
75+
</View>
76+
</>
77+
)}
8878
</SafeAreaView>
8979
);
9080
}
@@ -172,4 +162,17 @@ const styles = StyleSheet.create({
172162
alignItems: 'center',
173163
justifyContent: 'center',
174164
},
165+
mask: {
166+
height: 48,
167+
width: 210,
168+
},
169+
gradientContainer: {
170+
flex: 1,
171+
width: '300%',
172+
marginHorizontal: '-100%',
173+
},
174+
gradient: {
175+
flex: 1,
176+
width: '100%',
177+
},
175178
});

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+
}

0 commit comments

Comments
 (0)