VbeSkeleton.js 1.7 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677
  1. import React, { useEffect, useRef } from 'react';
  2. import { Animated, Easing, View } from 'react-native';
  3. const VbeSkeleton = ({
  4. style,
  5. color="rgba(0, 10, 20, 0.1)",
  6. layout=[],
  7. widthList=["100%"],
  8. height=16,
  9. margin={},
  10. borderRadius=8,
  11. animate=true,
  12. isLoading=true,
  13. children
  14. }) => {
  15. const opacity = useRef(new Animated.Value(1)).current;
  16. useEffect(() => {
  17. if (animate) {
  18. startScanAnimate()
  19. }
  20. }, [animate])
  21. const startScanAnimate = () => {
  22. Animated.timing(opacity, {
  23. toValue: 0.1,
  24. duration: 1000,
  25. easing: Easing.linear,
  26. useNativeDriver: true
  27. }).start(() => {
  28. if (animate) {
  29. endScanAnimate();
  30. }
  31. });
  32. }
  33. const endScanAnimate = () => {
  34. Animated.timing(opacity, {
  35. delay: 200,
  36. toValue: 1,
  37. duration: 1000,
  38. easing: Easing.linear,
  39. useNativeDriver: true
  40. }).start(() => {
  41. if (animate) {
  42. startScanAnimate();
  43. }
  44. });
  45. }
  46. if (isLoading) {
  47. if (layout.length > 0) {
  48. return (
  49. <Animated.View style={[style, {opacity}]}>
  50. { layout.map((item, index) =>
  51. <View key={index} style={{height, borderRadius: borderRadius, backgroundColor: color, ...item}}>
  52. </View>
  53. )}
  54. </Animated.View>
  55. )
  56. } else {
  57. return (
  58. <Animated.View style={[style, {opacity}]}>
  59. { widthList.map((item, index) =>
  60. <View key={index} style={{width: item, height, ...margin, borderRadius: borderRadius, backgroundColor: color ?? VbeTheme.getSkeletonColor()}}>
  61. </View>
  62. )}
  63. </Animated.View>
  64. );
  65. }
  66. } else if (children) {
  67. return children;
  68. }
  69. };
  70. export default VbeSkeleton;