Spaces:
Sleeping
Sleeping
File size: 2,059 Bytes
bb8f662 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 | import React, { useEffect, useRef } from 'react';
import { View, Animated, StyleSheet } from 'react-native';
import { LinearGradient } from 'expo-linear-gradient';
import { theme } from '../styles/theme';
export default function SkeletonLoader({
variant = 'text',
width = '100%',
height = 20,
style
}) {
const animatedValue = useRef(new Animated.Value(0)).current;
useEffect(() => {
Animated.loop(
Animated.sequence([
Animated.timing(animatedValue, {
toValue: 1,
duration: 1500,
useNativeDriver: true,
}),
Animated.timing(animatedValue, {
toValue: 0,
duration: 1500,
useNativeDriver: true,
}),
])
).start();
}, []);
const opacity = animatedValue.interpolate({
inputRange: [0, 1],
outputRange: [0.3, 0.7],
});
const getSkeletonStyle = () => {
switch (variant) {
case 'circular':
return {
width: width,
height: width,
borderRadius: width / 2,
};
case 'card':
return {
width: width,
height: height,
borderRadius: theme.borderRadius.lg,
};
case 'image':
return {
width: width,
height: height,
borderRadius: theme.borderRadius.md,
};
case 'text':
default:
return {
width: width,
height: height,
borderRadius: theme.borderRadius.sm,
};
}
};
return (
<Animated.View
style={[
styles.skeleton,
getSkeletonStyle(),
{ opacity },
style
]}
>
<LinearGradient
colors={[
theme.colors.surface,
theme.colors.card,
theme.colors.surface,
]}
start={{ x: 0, y: 0 }}
end={{ x: 1, y: 0 }}
style={StyleSheet.absoluteFill}
/>
</Animated.View>
);
}
const styles = StyleSheet.create({
skeleton: {
backgroundColor: theme.colors.surface,
overflow: 'hidden',
},
}); |