animated tab bar

This commit is contained in:
2025-10-03 10:43:08 +02:00
parent c6a5501cff
commit afd7cf9d45
3 changed files with 96 additions and 9 deletions
+68
View File
@@ -0,0 +1,68 @@
import React, { useCallback, useEffect } from "react";
import { Pressable } from "react-native";
import Animated, {
cancelAnimation,
useAnimatedStyle,
useSharedValue,
withSpring,
} from "react-native-reanimated";
export default function PressableScale({
children,
onPress,
style,
contentStyle,
hitSlop = 8,
disabled = false,
scaleTo = 1.5,
spring = { damping: 18, stiffness: 220, mass: 0.8 },
...rest
}) {
const sv = useSharedValue(1);
const rStyle = useAnimatedStyle(() => ({
transform: [{ scale: sv.value }],
}));
const pressIn = useCallback(() => {
if (disabled) return;
cancelAnimation(sv);
sv.value = 1;
sv.value = withSpring(scaleTo, spring);
}, [disabled, scaleTo, spring, sv]);
const reset = useCallback(() => {
cancelAnimation(sv);
sv.value = withSpring(1, spring);
}, [spring, sv]);
useEffect(
() => () => {
cancelAnimation(sv);
sv.value = 1;
},
[sv]
);
return (
<Pressable
onPressIn={pressIn}
onPressOut={reset}
onTouchCancel={reset}
onHoverOut={reset} // web
onMouseUp={reset} // web
onMouseLeave={reset} // web
onBlur={reset}
onPress={(e) => {
onPress?.(e);
reset();
}}
hitSlop={hitSlop}
disabled={disabled}
style={style}
{...rest}
>
<Animated.View style={[contentStyle, rStyle]}>{children}</Animated.View>
</Pressable>
);
}
+1
View File
@@ -25,6 +25,7 @@ export const BottomTabScreen = () => {
animate={{
tintColor: focused ? Palette.white : "#8B8883",
}}
transition={{ type: "spring", damping: 18, mass: 0.7 }}
style={[
{
width: Platform.OS === "web" ? 30 : 24,
+27 -9
View File
@@ -1,5 +1,6 @@
import { Motion } from "@legendapp/motion";
import * as Haptics from "expo-haptics";
import React from "react";
import React, { useState } from "react";
import { Text, TouchableOpacity, View } from "react-native";
import ItemContainer from "../components/ItemContainer/ItemContainer";
@@ -9,6 +10,7 @@ import { FONT_FAMILY } from "../styles/Fonts";
const TabBar = ({ state = {}, descriptors = {}, navigation = {} }) => {
const { isWeb } = useLayoutType();
const [pressedIndex, setPressedIndex] = useState(null);
return (
<View
@@ -87,18 +89,34 @@ const TabBar = ({ state = {}, descriptors = {}, navigation = {} }) => {
testID={options.tabBarTestID}
onPress={_onPressTabBar}
onLongPress={onLongPress}
onPressIn={() => setPressedIndex(index)}
onPressOut={() => setPressedIndex(null)}
>
{options?.tabBarIcon({ focused: isFocused })}
<Text
<Motion.View
animate={{
scale: isFocused
? 1.12
: pressedIndex === index
? 1.05
: 1,
}}
transition={{ type: "spring", damping: 18, mass: 0.7 }}
style={{
fontSize: isWeb ? 14 : 10,
color: isFocused ? Palette.white : "#8B8883",
fontFamily: FONT_FAMILY.HelveticaNeueRegular,
marginTop: 2,
...Style.containerCenter,
}}
>
{label}
</Text>
{options?.tabBarIcon({ focused: isFocused })}
<Text
style={{
fontSize: isWeb ? 14 : 10,
color: isFocused ? Palette.white : "#8B8883",
fontFamily: FONT_FAMILY.HelveticaNeueRegular,
marginTop: 2,
}}
>
{label}
</Text>
</Motion.View>
</TouchableOpacity>
)
);