import React, { memo, useMemo } from 'react'; import { StyleSheet, View } from 'react-native'; import Animated, { useSharedValue, withRepeat, withTiming, useAnimatedReaction, useDerivedValue, useAnimatedStyle, Easing, } from 'react-native-reanimated'; import { useLayout, useGetBones } from 'react-native-reanimated-skeleton/lib/hooks'; import TextView from './TextView'; export const ANIMATION_TYPE = { NONE: "none", SHIVER: "shiver", PULSE: "pulse" } export const ANIMATION_DIRECTION = { HORIZONTAL_LEFT: "horizontalLeft", HORIZONTAL_RIGHT: "horizontalRight", VERTICAL_TOP: "verticalTop", VERTICAL_DOWN: "verticalDown", DIAGONAL_DOWN_LEFT: "diagonalDownLeft", DIAGONAL_DOWN_RIGHT: "diagonalDownRight", DIAGONAL_TOP_LEFT: "diagonalTopLeft", DIAGONAL_TOP_RIGHT: "diagonalTopRight" } const VbeSkeleton = ({ style=styles.container, viewStyle={}, textStyle={}, text, duration=1200, layout=[], numberOfLines, animationType=ANIMATION_TYPE.SHIVER, animationDirection=ANIMATION_DIRECTION.HORIZONTAL_RIGHT, easing=Easing.bezier(0.5, 0, 0.25, 1), isLoading=true, boneColor='#eeeeee', highlightColor='#f6f6f6', hasFadeIn=false, children }) => { const animationValue = useSharedValue(0); const loadingValue = useSharedValue(0); const shiverValue = useSharedValue(animationType === 'shiver' ? 1 : 0); const [componentSize, onLayout] = useLayout(); const generalStyles = useMemo( () => ({ animationDirection, animationType, boneColor, animationValue, highlightColor, }), [ animationDirection, animationType, animationValue, boneColor, highlightColor, ], ); const getBones = useGetBones(componentSize); useAnimatedReaction( () => ({ isLoading, loadingValue }), () => { if (isLoading && loadingValue.value !== 1) { /* NOTE: Reset behaviour to ensure animation always starts from the beginning */ animationValue.value = 0; animationValue.value = shiverValue.value === 1 ? withRepeat(withTiming(1, { duration, easing }), -1, false) : withRepeat( withTiming(1, { duration: duration / 2, easing }), -1, true, ); } }, [isLoading, shiverValue], ); const opacity = useDerivedValue(() => { if (!isLoading) { return withTiming(1, { duration: 250 }); } return 0; }); const animatedStyle = useAnimatedStyle(() => ({ opacity: opacity.value, })); return ( {isLoading ? ( getBones({ bonesLayout: layout, children, generalStyles, }) ) : ( { text ? {text} : children } )} ); }; export default VbeSkeleton; const styles = StyleSheet.create({ container: { flex: 1, alignItems: 'center', justifyContent: 'center' }, viewStyle: { alignItems: 'center' } });