@freakycoder/animated-tabbar
Version:
A 60FPS animated tab bar with a variety of cool animation presets.
128 lines • 4.87 kB
JavaScript
import React, { useMemo, memo } from 'react';
import { View, Text } from 'react-native';
import Animated, { useSharedValue, useAnimatedStyle, interpolate, Extrapolate } from 'react-native-reanimated';
import MaskedView from '@react-native-community/masked-view';
import { Svg, Circle } from 'react-native-svg';
import isEqual from 'lodash.isequal';
import { DEFAULT_INDICATOR_VISIBILITY, DEFAULT_INDICATOR_SIZE, DEFAULT_INDICATOR_COLOR } from '../constants';
import { styles } from './styles';
const AnimatedSvg = Animated.createAnimatedComponent(Svg);
const AnimatedCircle = Animated.createAnimatedComponent(Circle);
const FlashyTabBarItemComponent = ({
animatedFocus,
label,
icon,
labelStyle: labelStyleOverride,
spacing,
iconSize,
indicator,
isRTL
}) => {
const {
innerVerticalSpace,
innerHorizontalSpace,
outerVerticalSpace,
outerHorizontalSpace
} = spacing;
const {
size: _indicatorSize,
color: _indicatorColor,
visible: _indicatorVisible
} = indicator || {
size: undefined,
color: undefined,
visible: undefined
};
const {
indicatorVisibility,
indicatorColor,
indicatorSize
} = useMemo(() => {
var _ref;
return {
indicatorVisibility: _indicatorVisible !== null && _indicatorVisible !== void 0 ? _indicatorVisible : DEFAULT_INDICATOR_VISIBILITY,
indicatorColor: (_ref = _indicatorColor !== null && _indicatorColor !== void 0 ? _indicatorColor : labelStyleOverride === null || labelStyleOverride === void 0 ? void 0 : labelStyleOverride.color) !== null && _ref !== void 0 ? _ref : DEFAULT_INDICATOR_COLOR,
indicatorSize: _indicatorSize !== null && _indicatorSize !== void 0 ? _indicatorSize : DEFAULT_INDICATOR_SIZE
};
}, [_indicatorVisible, _indicatorColor, _indicatorSize, labelStyleOverride]);
const labelWidth = useSharedValue(0);
const labelHeight = useSharedValue(0);
const containerHeight = useMemo(() => iconSize + innerVerticalSpace * 2, [iconSize, innerVerticalSpace]);
const containerWidth = useSharedValue(iconSize + innerHorizontalSpace * 2);
const labelContainerStyle = useAnimatedStyle(() => ({
transform: [{
translateY: interpolate(animatedFocus.value, [0, 1], [labelHeight.value * 0.5, labelHeight.value * -0.5], Extrapolate.CLAMP)
}]
}));
const iconContainerStyle = useAnimatedStyle(() => ({
transform: [{
translateY: interpolate(animatedFocus.value, [0, 1], [iconSize * -0.5, iconSize * -1.5], Extrapolate.CLAMP)
}]
}));
const handleLabelLayout = ({
nativeEvent: {
layout: {
height,
width
}
}
}) => {
labelWidth.value = width;
labelHeight.value = height;
containerWidth.value = Math.max(width + innerHorizontalSpace * 2, containerWidth.value);
};
const renderIcon = () => {
const IconComponent = icon.component;
return typeof IconComponent === 'function' ? /*#__PURE__*/React.createElement(IconComponent, {
animatedFocus: animatedFocus.value,
color: icon.color,
size: iconSize
}) : /*#__PURE__*/React.createElement(IconComponent, {
animatedFocus: animatedFocus.value,
color: icon.color,
size: iconSize
});
};
return /*#__PURE__*/React.createElement(Animated.View, {
style: [styles.outerContainer, {
paddingHorizontal: outerHorizontalSpace,
paddingVertical: outerVerticalSpace
}]
}, /*#__PURE__*/React.createElement(Animated.View, {
style: [styles.container, {
width: containerWidth.value,
height: containerHeight
}]
}, /*#__PURE__*/React.createElement(MaskedView, {
style: styles.root,
maskElement: /*#__PURE__*/React.createElement(Animated.View, {
style: iconContainerStyle
})
}, /*#__PURE__*/React.createElement(Animated.View, {
pointerEvents: "none",
style: iconContainerStyle
}, /*#__PURE__*/React.createElement(View, {
style: styles.icon
}, renderIcon()))), /*#__PURE__*/React.createElement(MaskedView, {
style: styles.root,
maskElement: /*#__PURE__*/React.createElement(Animated.View, {
style: labelContainerStyle
})
}, /*#__PURE__*/React.createElement(Animated.View, {
style: labelContainerStyle
}, /*#__PURE__*/React.createElement(Text, {
numberOfLines: 1,
style: [styles.label, labelStyleOverride],
onLayout: handleLabelLayout
}, label))), indicatorVisibility && /*#__PURE__*/React.createElement(AnimatedSvg, {
style: [styles.root, {
left: containerWidth.value / 2 - indicatorSize / 2,
top: containerHeight - indicatorSize
}]
}, /*#__PURE__*/React.createElement(AnimatedCircle, {
r: interpolate(animatedFocus.value, [0.5, 1], [0, indicatorSize / 2], Extrapolate.CLAMP),
fill: indicatorColor
}))));
};
export default /*#__PURE__*/memo(FlashyTabBarItemComponent, isEqual);
//# sourceMappingURL=FlashyTabBarItem.js.map